Repository: enilu/material-admin Branch: master Commit: 82fa7e8ca363 Files: 757 Total size: 7.6 MB Directory structure: gitextract_tobjk67h/ ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── doc/ │ ├── .vuepress/ │ │ └── config.js │ ├── README.md │ ├── action/ │ │ ├── cache.md │ │ ├── jpaauditing.md │ │ ├── sqlite.md │ │ └── task.md │ ├── base/ │ │ ├── jdkAndMaven.md │ │ ├── modules.md │ │ └── preface.md │ ├── config/ │ │ ├── application.md │ │ ├── beetl.md │ │ ├── ehcache.md │ │ ├── logback.md │ │ ├── shiro.md │ │ └── swagger.md │ ├── donate.md │ ├── ecosystem/ │ │ ├── code-generator.md │ │ └── database-doc-generator.md │ ├── feature/ │ │ ├── dict.md │ │ ├── log.md │ │ ├── menu.md │ │ ├── modules.md │ │ └── monitor.md │ ├── helloworld/ │ │ ├── add.md │ │ ├── base.md │ │ ├── create_table.md │ │ ├── crud.md │ │ ├── delete.md │ │ ├── list.md │ │ ├── menuAndPermission.md │ │ └── update.md │ ├── other/ │ │ └── faq.md │ ├── package.json │ ├── quickstart/ │ │ ├── clone.md │ │ ├── config.md │ │ ├── initDb.md │ │ ├── quickstart.md │ │ └── startup.md │ ├── resource.md │ └── 文档完善中 ├── material-core/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── cn/ │ │ └── enilu/ │ │ └── material/ │ │ ├── aop/ │ │ │ ├── LogAop.java │ │ │ └── PermissionAop.java │ │ ├── bean/ │ │ │ ├── constant/ │ │ │ │ ├── Const.java │ │ │ │ ├── cache/ │ │ │ │ │ ├── Cache.java │ │ │ │ │ └── CacheKey.java │ │ │ │ ├── factory/ │ │ │ │ │ └── PageFactory.java │ │ │ │ ├── package-info.java │ │ │ │ └── state/ │ │ │ │ ├── BizLogType.java │ │ │ │ ├── LogSucceed.java │ │ │ │ ├── LogType.java │ │ │ │ ├── ManagerStatus.java │ │ │ │ ├── MenuStatus.java │ │ │ │ └── Order.java │ │ │ ├── core/ │ │ │ │ ├── BussinessLog.java │ │ │ │ ├── Permission.java │ │ │ │ └── ShiroUser.java │ │ │ ├── dictmap/ │ │ │ │ ├── CfgDict.java │ │ │ │ ├── CommonDict.java │ │ │ │ ├── DeleteDict.java │ │ │ │ ├── DeptDict.java │ │ │ │ ├── DictMap.java │ │ │ │ ├── LogDict.java │ │ │ │ ├── MenuDict.java │ │ │ │ ├── NoticeMap.java │ │ │ │ ├── RoleDict.java │ │ │ │ ├── SystemDict.java │ │ │ │ ├── TaskDict.java │ │ │ │ ├── UserDict.java │ │ │ │ └── base/ │ │ │ │ └── AbstractDictMap.java │ │ │ ├── dto/ │ │ │ │ └── UserDto.java │ │ │ ├── entity/ │ │ │ │ ├── BaseEntity.java │ │ │ │ ├── message/ │ │ │ │ │ ├── Message.java │ │ │ │ │ ├── MessageSender.java │ │ │ │ │ └── MessageTemplate.java │ │ │ │ ├── system/ │ │ │ │ │ ├── Cfg.java │ │ │ │ │ ├── Dept.java │ │ │ │ │ ├── Dict.java │ │ │ │ │ ├── FileInfo.java │ │ │ │ │ ├── LoginLog.java │ │ │ │ │ ├── Menu.java │ │ │ │ │ ├── Notice.java │ │ │ │ │ ├── OperationLog.java │ │ │ │ │ ├── Relation.java │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── Task.java │ │ │ │ │ ├── TaskLog.java │ │ │ │ │ └── User.java │ │ │ │ └── test/ │ │ │ │ └── Boy.java │ │ │ ├── enumeration/ │ │ │ │ ├── BizExceptionEnum.java │ │ │ │ ├── ConfigKeyEnum.java │ │ │ │ ├── ProjectEnum.java │ │ │ │ ├── RedisQueueName.java │ │ │ │ ├── SerialNumberEnum.java │ │ │ │ ├── TypeEnum.java │ │ │ │ └── cms/ │ │ │ │ ├── BannerTypeEnum.java │ │ │ │ └── ChannelEnum.java │ │ │ ├── exception/ │ │ │ │ ├── ApplicationException.java │ │ │ │ ├── ExceptionEnum.java │ │ │ │ ├── InvalidKaptchaException.java │ │ │ │ ├── MailException.java │ │ │ │ ├── ParamException.java │ │ │ │ ├── ServiceExceptionEnum.java │ │ │ │ ├── SlConnectException.java │ │ │ │ ├── SlEvalException.java │ │ │ │ ├── ValidException.java │ │ │ │ ├── XSException.java │ │ │ │ └── XSRuntimeException.java │ │ │ └── vo/ │ │ │ ├── DictVo.java │ │ │ ├── QuartzJob.java │ │ │ ├── SpringContextHolder.java │ │ │ ├── front/ │ │ │ │ ├── Ret.java │ │ │ │ └── Rets.java │ │ │ ├── node/ │ │ │ │ ├── DeptNode.java │ │ │ │ ├── IsMenu.java │ │ │ │ ├── MenuNode.java │ │ │ │ ├── Node.java │ │ │ │ └── ZTreeNode.java │ │ │ └── query/ │ │ │ ├── DynamicSpecifications.java │ │ │ ├── MutiStrFactory.java │ │ │ ├── Page.java │ │ │ └── SearchFilter.java │ │ ├── dao/ │ │ │ ├── BaseRepository.java │ │ │ ├── BaseRepositoryFactoryBean.java │ │ │ ├── BaseRepositoryImpl.java │ │ │ ├── DaoConfiguration.java │ │ │ ├── cache/ │ │ │ │ ├── BaseCache.java │ │ │ │ ├── Cache.java │ │ │ │ ├── CacheDao.java │ │ │ │ ├── ConfigCache.java │ │ │ │ ├── DictCache.java │ │ │ │ ├── TokenCache.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ConfigCacheImpl.java │ │ │ │ │ ├── DictCacheImpl.java │ │ │ │ │ └── EhcacheDao.java │ │ │ │ └── package-info.java │ │ │ ├── message/ │ │ │ │ ├── MessageRepository.java │ │ │ │ ├── MessagesenderRepository.java │ │ │ │ └── MessagetemplateRepository.java │ │ │ └── system/ │ │ │ ├── CfgRepository.java │ │ │ ├── DeptRepository.java │ │ │ ├── DictRepository.java │ │ │ ├── FileInfoRepository.java │ │ │ ├── LoginLogRepository.java │ │ │ ├── MenuRepository.java │ │ │ ├── OperationLogRepository.java │ │ │ ├── RelationRepository.java │ │ │ ├── RoleRepository.java │ │ │ ├── SysNoticeRepository.java │ │ │ ├── TaskLogRepository.java │ │ │ ├── TaskRepository.java │ │ │ └── UserRepository.java │ │ ├── factory/ │ │ │ ├── DictFieldWarpperFactory.java │ │ │ └── UserFactory.java │ │ ├── platform/ │ │ │ └── package-info.java │ │ ├── service/ │ │ │ ├── BaseService.java │ │ │ ├── CrudService.java │ │ │ ├── DeleteService.java │ │ │ ├── InsertService.java │ │ │ ├── SelectService.java │ │ │ ├── UpdateService.java │ │ │ ├── message/ │ │ │ │ ├── MessageService.java │ │ │ │ ├── MessagesenderService.java │ │ │ │ ├── MessagetemplateService.java │ │ │ │ ├── email/ │ │ │ │ │ ├── DefaultEmailSender.java │ │ │ │ │ └── EmailSender.java │ │ │ │ └── sms/ │ │ │ │ ├── SmsSender.java │ │ │ │ └── tencent/ │ │ │ │ └── TencentSmsSender.java │ │ │ ├── system/ │ │ │ │ ├── AccountService.java │ │ │ │ ├── CfgService.java │ │ │ │ ├── DeptService.java │ │ │ │ ├── DictService.java │ │ │ │ ├── FileService.java │ │ │ │ ├── IConstantFactory.java │ │ │ │ ├── LogObjectHolder.java │ │ │ │ ├── LoginLogService.java │ │ │ │ ├── MenuService.java │ │ │ │ ├── NoticeService.java │ │ │ │ ├── OperationLogService.java │ │ │ │ ├── RoleService.java │ │ │ │ ├── UserService.java │ │ │ │ └── impl/ │ │ │ │ └── ConstantFactory.java │ │ │ └── task/ │ │ │ ├── BaseJob.java │ │ │ ├── JobExecuter.java │ │ │ ├── JobService.java │ │ │ ├── NoConurrentBaseJob.java │ │ │ ├── QuartzConfigration.java │ │ │ ├── TaskService.java │ │ │ ├── TaskUtils.java │ │ │ └── job/ │ │ │ └── HelloJob.java │ │ ├── shiro/ │ │ │ ├── ShiroDbRealm.java │ │ │ ├── ShiroKit.java │ │ │ ├── check/ │ │ │ │ ├── ICheck.java │ │ │ │ ├── PermissionCheckFactory.java │ │ │ │ └── PermissionCheckManager.java │ │ │ └── factory/ │ │ │ ├── IShiro.java │ │ │ └── ShiroFactroy.java │ │ ├── utils/ │ │ │ ├── BasicType.java │ │ │ ├── BeanUtil.java │ │ │ ├── BirthUtils.java │ │ │ ├── CollectionKit.java │ │ │ ├── Constants.java │ │ │ ├── Convert.java │ │ │ ├── CryptUtils.java │ │ │ ├── DateTime.java │ │ │ ├── DateTimeKit.java │ │ │ ├── DateUtil.java │ │ │ ├── HexKit.java │ │ │ ├── HttpKit.java │ │ │ ├── Lists.java │ │ │ ├── Log.java │ │ │ ├── MD5.java │ │ │ ├── Maps.java │ │ │ ├── MobileUtil.java │ │ │ ├── PageKit.java │ │ │ ├── RSAUtil.java │ │ │ ├── RandomUtils.java │ │ │ ├── StrKit.java │ │ │ ├── StringUtils.java │ │ │ ├── ToolUtil.java │ │ │ ├── WafKit.java │ │ │ ├── WafRequestWrapper.java │ │ │ ├── XmlHelper.java │ │ │ ├── ZipUtils.java │ │ │ └── cache/ │ │ │ ├── TimeCacheMap.java │ │ │ └── exception/ │ │ │ └── ToolBoxException.java │ │ ├── warpper/ │ │ │ ├── BaseControllerWarpper.java │ │ │ ├── DeptWarpper.java │ │ │ ├── DictWarpper.java │ │ │ ├── LogWarpper.java │ │ │ ├── MenuWarpper.java │ │ │ ├── NoticeWrapper.java │ │ │ ├── RoleWarpper.java │ │ │ └── UserWarpper.java │ │ └── web/ │ │ ├── listener/ │ │ │ ├── CacheListener.java │ │ │ └── ConfigListener.java │ │ └── package-info.java │ └── resources/ │ └── code/ │ └── code.json ├── material-generator/ │ ├── README.md │ ├── README.zh-CN.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── cn/ │ │ └── enilu/ │ │ └── flash/ │ │ └── code/ │ │ ├── AbstractLoader.java │ │ ├── CodeConfig.java │ │ ├── ColumnDescriptor.java │ │ ├── EntityDescLoader.java │ │ ├── Generator.java │ │ ├── Loader.java │ │ ├── StrKit.java │ │ ├── TableDescLoader.java │ │ ├── TableDescriptor.java │ │ └── Utils.java │ └── resources/ │ └── code/ │ ├── code.json │ ├── controller.vm │ ├── repository.vm │ ├── service.vm │ └── view/ │ ├── add.html.vm │ ├── edit.html.vm │ ├── index.html.vm │ ├── index.js.vm │ └── info.js.vm ├── material-lab/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── resources/ │ └── application.properties ├── material-manage/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── cn/ │ │ └── enilu/ │ │ └── material/ │ │ └── admin/ │ │ ├── AdminApplication.java │ │ ├── AdminServletInitializer.java │ │ ├── common/ │ │ │ └── constant/ │ │ │ ├── enums/ │ │ │ │ └── Status.java │ │ │ └── state/ │ │ │ ├── ExpenseState.java │ │ │ ├── IsMenu.java │ │ │ └── MenuOpenStatus.java │ │ ├── config/ │ │ │ ├── DefaultFastjsonConfig.java │ │ │ ├── DruidConfig.java │ │ │ ├── EhCacheConfig.java │ │ │ ├── SpringSessionConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ ├── UserIDAuditorConfig.java │ │ │ ├── properties/ │ │ │ │ ├── AppProperties.java │ │ │ │ └── BeetlProperties.java │ │ │ └── web/ │ │ │ ├── BeetlConfig.java │ │ │ └── ShiroConfig.java │ │ ├── core/ │ │ │ ├── CoreFlag.java │ │ │ ├── aop/ │ │ │ │ ├── BaseControllerExceptionHandler.java │ │ │ │ └── GlobalExceptionHandler.java │ │ │ ├── base/ │ │ │ │ ├── controller/ │ │ │ │ │ ├── BaseController.java │ │ │ │ │ ├── ErrorView.java │ │ │ │ │ └── GlobalController.java │ │ │ │ └── tips/ │ │ │ │ ├── ErrorTip.java │ │ │ │ ├── SuccessTip.java │ │ │ │ └── Tip.java │ │ │ ├── beetl/ │ │ │ │ ├── BeetlConfiguration.java │ │ │ │ └── ShiroExt.java │ │ │ ├── cache/ │ │ │ │ ├── BaseCacheFactory.java │ │ │ │ ├── CacheKit.java │ │ │ │ ├── EhcacheFactory.java │ │ │ │ ├── ICache.java │ │ │ │ └── ILoader.java │ │ │ ├── datascope/ │ │ │ │ └── DataScope.java │ │ │ ├── datasource/ │ │ │ │ └── DruidProperties.java │ │ │ ├── intercept/ │ │ │ │ ├── SessionInterceptor.java │ │ │ │ └── SessionTimeoutInterceptor.java │ │ │ ├── page/ │ │ │ │ ├── PageBT.java │ │ │ │ └── PageInfoBT.java │ │ │ ├── qr/ │ │ │ │ ├── ImgQrTool.java │ │ │ │ ├── MatrixToImageConfig.java │ │ │ │ ├── MatrixToImageWriter.java │ │ │ │ └── QrImage.java │ │ │ ├── support/ │ │ │ │ ├── BasicType.java │ │ │ │ ├── BeanKit.java │ │ │ │ ├── ClassKit.java │ │ │ │ ├── CollectionKit.java │ │ │ │ ├── DateTime.java │ │ │ │ ├── DateTimeKit.java │ │ │ │ ├── HexKit.java │ │ │ │ ├── ObjectKit.java │ │ │ │ ├── PageKit.java │ │ │ │ └── exception/ │ │ │ │ └── ToolBoxException.java │ │ │ ├── util/ │ │ │ │ ├── ApiMenuFilter.java │ │ │ │ ├── FileUtil.java │ │ │ │ ├── HttpSessionHolder.java │ │ │ │ ├── KaptchaUtil.java │ │ │ │ ├── NumUtil.java │ │ │ │ ├── PingYinUtil.java │ │ │ │ ├── RenderUtil.java │ │ │ │ ├── ResKit.java │ │ │ │ └── SqlUtil.java │ │ │ └── xss/ │ │ │ ├── XssFilter.java │ │ │ └── XssHttpServletRequestWrapper.java │ │ ├── modular/ │ │ │ ├── lab/ │ │ │ │ ├── controller/ │ │ │ │ │ └── LabController.java │ │ │ │ └── package-info.java │ │ │ ├── message/ │ │ │ │ ├── MessageController.java │ │ │ │ ├── MessagesenderController.java │ │ │ │ └── MessagetemplateController.java │ │ │ └── system/ │ │ │ ├── controller/ │ │ │ │ ├── BlackboardController.java │ │ │ │ ├── CfgController.java │ │ │ │ ├── DeptController.java │ │ │ │ ├── DictController.java │ │ │ │ ├── KaptchaController.java │ │ │ │ ├── LogController.java │ │ │ │ ├── LoginController.java │ │ │ │ ├── LoginLogController.java │ │ │ │ ├── MenuController.java │ │ │ │ ├── NoticeController.java │ │ │ │ ├── RoleController.java │ │ │ │ ├── TaskController.java │ │ │ │ └── UserMgrController.java │ │ │ └── transfer/ │ │ │ ├── ManagerUser.java │ │ │ ├── ReqAddManager.java │ │ │ └── ReqEditManager.java │ │ └── runner/ │ │ └── StartJob.java │ ├── resources/ │ │ ├── META-INF/ │ │ │ └── spring-devtools.properties │ │ ├── application-dev.properties │ │ ├── application-lab.properties │ │ ├── application-prod.properties │ │ ├── application.properties │ │ ├── banner.txt │ │ ├── ehcache.xml │ │ ├── import.sql │ │ └── logback.xml │ └── webapp/ │ ├── WEB-INF/ │ │ └── view/ │ │ ├── 404.html │ │ ├── common/ │ │ │ ├── _chat.html │ │ │ ├── _footer.html │ │ │ ├── _header.html │ │ │ ├── _sidebar.html │ │ │ ├── include.html │ │ │ ├── layout.html │ │ │ └── tags/ │ │ │ ├── NameCon.tag │ │ │ ├── SelectCon.tag │ │ │ ├── TimeCon.tag │ │ │ ├── avatar.tag │ │ │ ├── button.tag │ │ │ ├── datePicker.tag │ │ │ ├── input.tag │ │ │ ├── select.tag │ │ │ ├── table.tag │ │ │ └── textarea.tag │ │ ├── index.html │ │ ├── lab/ │ │ │ ├── actuator.html │ │ │ └── gis.html │ │ ├── login.html │ │ ├── message/ │ │ │ ├── history/ │ │ │ │ ├── message.html │ │ │ │ └── message_view.html │ │ │ ├── sender/ │ │ │ │ ├── sender.html │ │ │ │ ├── sender_add.html │ │ │ │ └── sender_edit.html │ │ │ └── template/ │ │ │ ├── template.html │ │ │ ├── template_add.html │ │ │ └── template_edit.html │ │ └── system/ │ │ ├── cfg/ │ │ │ ├── cfg.html │ │ │ ├── cfg_add.html │ │ │ └── cfg_edit.html │ │ ├── dept/ │ │ │ ├── dept.html │ │ │ ├── dept_add.html │ │ │ └── dept_edit.html │ │ ├── dict/ │ │ │ ├── dict.html │ │ │ ├── dict_add.html │ │ │ └── dict_edit.html │ │ ├── menu/ │ │ │ ├── menu.html │ │ │ ├── menu_add.html │ │ │ └── menu_edit.html │ │ ├── role/ │ │ │ ├── role.html │ │ │ ├── role_add.html │ │ │ ├── role_assign.html │ │ │ └── role_edit.html │ │ ├── task/ │ │ │ ├── task.html │ │ │ ├── task_add.html │ │ │ ├── task_edit.html │ │ │ └── task_log.html │ │ └── user/ │ │ ├── user.html │ │ ├── user_add.html │ │ ├── user_chpwd.html │ │ ├── user_edit.html │ │ ├── user_roleassign.html │ │ └── user_view.html │ └── static/ │ ├── css/ │ │ ├── _fstyle.css │ │ ├── app.css │ │ ├── app.min.1.css │ │ ├── app.min.2.css │ │ ├── font-awesome.css │ │ ├── material.css │ │ └── plugins/ │ │ ├── dataTables/ │ │ │ └── dataTables.bootstrap.css │ │ ├── jquery-treegrid/ │ │ │ └── css/ │ │ │ └── jquery.treegrid.css │ │ ├── webuploader/ │ │ │ └── webuploader.css │ │ └── ztree/ │ │ ├── demo.css │ │ └── zTreeStyle.css │ ├── fonts/ │ │ └── FontAwesome.otf │ ├── js/ │ │ ├── charts.js │ │ ├── common/ │ │ │ ├── Feng.js │ │ │ ├── ajax-object.js │ │ │ ├── bootstrap-table-object.js │ │ │ ├── tree-table-object.js │ │ │ ├── web-upload-object.js │ │ │ └── ztree-object.js │ │ ├── demo.js │ │ ├── flot-charts/ │ │ │ ├── bar-chart.js │ │ │ ├── curved-line-chart.js │ │ │ ├── dynamic-chart.js │ │ │ ├── line-chart.js │ │ │ └── pie-chart.js │ │ ├── functions.js │ │ ├── hplus.js │ │ ├── plugins/ │ │ │ ├── bootstrap-table/ │ │ │ │ └── locale/ │ │ │ │ └── bootstrap-table-zh-CN.js │ │ │ ├── dataTables/ │ │ │ │ ├── dataTables.bootstrap.js │ │ │ │ └── jquery.dataTables.js │ │ │ ├── jquery-treegrid/ │ │ │ │ ├── extension/ │ │ │ │ │ └── jquery.treegrid.extension.js │ │ │ │ └── js/ │ │ │ │ └── jquery.treegrid.bootstrap3.js │ │ │ ├── layer/ │ │ │ │ ├── extend/ │ │ │ │ │ └── layer.ext.js │ │ │ │ ├── laydate/ │ │ │ │ │ ├── laydate.js │ │ │ │ │ ├── need/ │ │ │ │ │ │ └── laydate.css │ │ │ │ │ └── skins/ │ │ │ │ │ └── default/ │ │ │ │ │ └── laydate.css │ │ │ │ ├── layim/ │ │ │ │ │ ├── layim.css │ │ │ │ │ └── layim.js │ │ │ │ ├── mobile/ │ │ │ │ │ ├── layer.js │ │ │ │ │ └── need/ │ │ │ │ │ └── layer.css │ │ │ │ ├── skin/ │ │ │ │ │ ├── layer.css │ │ │ │ │ └── layer.ext.css │ │ │ │ └── theme/ │ │ │ │ └── default/ │ │ │ │ └── layer.css │ │ │ ├── validate/ │ │ │ │ └── zh_CN.js │ │ │ └── webuploader/ │ │ │ ├── README.md │ │ │ ├── Uploader.swf │ │ │ ├── webuploader.css │ │ │ ├── webuploader.custom.js │ │ │ ├── webuploader.fis.js │ │ │ ├── webuploader.flashonly.js │ │ │ ├── webuploader.html5only.js │ │ │ ├── webuploader.js │ │ │ ├── webuploader.noimage.js │ │ │ ├── webuploader.nolog.js │ │ │ └── webuploader.withoutimage.js │ │ └── read-me.txt │ ├── lab/ │ │ ├── README.md │ │ └── gis/ │ │ ├── base/ │ │ │ ├── base_leaflet.draw.ext.js │ │ │ ├── base_map.js │ │ │ └── base_tilesUtils.js │ │ ├── index.js │ │ ├── js/ │ │ │ ├── base/ │ │ │ │ ├── base_leaflet.draw.ext.js │ │ │ │ ├── base_map.js │ │ │ │ └── base_tilesUtils.js │ │ │ ├── jslib/ │ │ │ │ ├── LMapLib.js │ │ │ │ ├── Leaflet/ │ │ │ │ │ ├── leaflet.css │ │ │ │ │ ├── leaflet.functionaltilelayer.js │ │ │ │ │ ├── leaflet.js │ │ │ │ │ └── leaflet.markercluster.js │ │ │ │ ├── Leaflet.PolylineDecorator-master/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── example/ │ │ │ │ │ │ ├── example.html │ │ │ │ │ │ └── example.js │ │ │ │ │ ├── leaflet.polylineDecorator.js │ │ │ │ │ └── src/ │ │ │ │ │ ├── L.LineUtil.PolylineDecorator.js │ │ │ │ │ ├── L.PolylineDecorator.js │ │ │ │ │ ├── L.RotatedMarker.js │ │ │ │ │ └── L.Symbol.js │ │ │ │ ├── Leaflet.draw/ │ │ │ │ │ ├── leaflet.draw-src.js │ │ │ │ │ ├── leaflet.draw.css │ │ │ │ │ └── leaflet.draw.js │ │ │ │ ├── Leaflet.plugins/ │ │ │ │ │ └── arc.js │ │ │ │ ├── arc.js-gh-pages/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── arc.js │ │ │ │ │ ├── bezier.geojson │ │ │ │ │ ├── bezier.html │ │ │ │ │ ├── bezier.js │ │ │ │ │ ├── bezier.py │ │ │ │ │ ├── example/ │ │ │ │ │ │ ├── csv2arc.js │ │ │ │ │ │ ├── round.js │ │ │ │ │ │ ├── routes.csv │ │ │ │ │ │ └── tracks.geojson │ │ │ │ │ ├── index.html │ │ │ │ │ ├── index.js │ │ │ │ │ ├── package.json │ │ │ │ │ ├── polymaps.js │ │ │ │ │ ├── sphericalmercator.js │ │ │ │ │ └── test.js │ │ │ │ ├── arcDecorator.js │ │ │ │ ├── bar.js │ │ │ │ └── mapColumnChart.js │ │ │ └── test/ │ │ │ ├── data/ │ │ │ │ └── cirlData.js │ │ │ └── demo.js │ │ └── leaflet/ │ │ ├── L.Control.Zoominfo.css │ │ ├── L.Control.Zoominfo.js │ │ ├── leaflet.css │ │ └── leaflet.js │ ├── less/ │ │ ├── app.less │ │ └── inc/ │ │ ├── 404.less │ │ ├── alert.less │ │ ├── base.less │ │ ├── bootstrap-master/ │ │ │ ├── alerts.less │ │ │ ├── badges.less │ │ │ ├── bootstrap.less │ │ │ ├── breadcrumbs.less │ │ │ ├── button-groups.less │ │ │ ├── buttons.less │ │ │ ├── carousel.less │ │ │ ├── close.less │ │ │ ├── code.less │ │ │ ├── component-animations.less │ │ │ ├── dropdowns.less │ │ │ ├── forms.less │ │ │ ├── glyphicons.less │ │ │ ├── grid.less │ │ │ ├── input-groups.less │ │ │ ├── jumbotron.less │ │ │ ├── labels.less │ │ │ ├── list-group.less │ │ │ ├── media.less │ │ │ ├── mixins/ │ │ │ │ ├── alerts.less │ │ │ │ ├── background-variant.less │ │ │ │ ├── border-radius.less │ │ │ │ ├── buttons.less │ │ │ │ ├── center-block.less │ │ │ │ ├── clearfix.less │ │ │ │ ├── forms.less │ │ │ │ ├── gradients.less │ │ │ │ ├── grid-framework.less │ │ │ │ ├── grid.less │ │ │ │ ├── hide-text.less │ │ │ │ ├── image.less │ │ │ │ ├── labels.less │ │ │ │ ├── list-group.less │ │ │ │ ├── nav-divider.less │ │ │ │ ├── nav-vertical-align.less │ │ │ │ ├── opacity.less │ │ │ │ ├── pagination.less │ │ │ │ ├── panels.less │ │ │ │ ├── progress-bar.less │ │ │ │ ├── reset-filter.less │ │ │ │ ├── resize.less │ │ │ │ ├── responsive-visibility.less │ │ │ │ ├── size.less │ │ │ │ ├── tab-focus.less │ │ │ │ ├── table-row.less │ │ │ │ ├── text-emphasis.less │ │ │ │ ├── text-overflow.less │ │ │ │ └── vendor-prefixes.less │ │ │ ├── mixins.less │ │ │ ├── modals.less │ │ │ ├── navbar.less │ │ │ ├── navs.less │ │ │ ├── normalize.less │ │ │ ├── pager.less │ │ │ ├── pagination.less │ │ │ ├── panels.less │ │ │ ├── popovers.less │ │ │ ├── print.less │ │ │ ├── progress-bars.less │ │ │ ├── responsive-embed.less │ │ │ ├── responsive-utilities.less │ │ │ ├── scaffolding.less │ │ │ ├── tables.less │ │ │ ├── theme.less │ │ │ ├── thumbnails.less │ │ │ ├── tooltip.less │ │ │ ├── type.less │ │ │ ├── utilities.less │ │ │ ├── variables.less │ │ │ └── wells.less │ │ ├── bootstrap-overrides.less │ │ ├── breadcrumb.less │ │ ├── button.less │ │ ├── card.less │ │ ├── chart.less │ │ ├── chat.less │ │ ├── contacts.less │ │ ├── dropdown.less │ │ ├── font.less │ │ ├── footer.less │ │ ├── form.less │ │ ├── generics.less │ │ ├── header.less │ │ ├── ie-warning.less │ │ ├── invoice.less │ │ ├── less-plugins/ │ │ │ └── for.less │ │ ├── list.less │ │ ├── listview.less │ │ ├── login.less │ │ ├── media.less │ │ ├── messages.less │ │ ├── misc.less │ │ ├── mixin.less │ │ ├── modal.less │ │ ├── pagination.less │ │ ├── panel.less │ │ ├── photos.less │ │ ├── popover.less │ │ ├── preloader.less │ │ ├── pricing-table.less │ │ ├── print.less │ │ ├── profile.less │ │ ├── progress-bar.less │ │ ├── shadow.less │ │ ├── sidebar.less │ │ ├── skin.less │ │ ├── table.less │ │ ├── tabs.less │ │ ├── todo.less │ │ ├── tooltip.less │ │ ├── variables.less │ │ ├── vendor-overrides/ │ │ │ ├── bootgrid.less │ │ │ ├── bootstrap-datetimepicker.less │ │ │ ├── bootstrap-select.less │ │ │ ├── chosen.less │ │ │ ├── farbtastic.less │ │ │ ├── fileinput.less │ │ │ ├── fullcalendar.less │ │ │ ├── light-gallery.less │ │ │ ├── malihu-custom-scrollbar.less │ │ │ ├── mediaelement.less │ │ │ ├── noUiSlider.less │ │ │ ├── summernote.less │ │ │ ├── sweetalert.less │ │ │ ├── typeahead.less │ │ │ └── waves.less │ │ ├── wall.less │ │ ├── widgets.less │ │ └── wizard.less │ ├── modular/ │ │ ├── message/ │ │ │ ├── history/ │ │ │ │ └── message.js │ │ │ ├── sender/ │ │ │ │ ├── sender.js │ │ │ │ └── sender_info.js │ │ │ └── template/ │ │ │ ├── template.js │ │ │ └── template_info.js │ │ └── system/ │ │ ├── cfg/ │ │ │ ├── cfg.js │ │ │ └── cfg_info.js │ │ ├── dept/ │ │ │ ├── dept.js │ │ │ └── dept_info.js │ │ ├── dict/ │ │ │ ├── dict.js │ │ │ └── dict_info.js │ │ ├── menu/ │ │ │ ├── menu.js │ │ │ └── menu_info.js │ │ ├── role/ │ │ │ ├── role.js │ │ │ └── role_info.js │ │ ├── task/ │ │ │ ├── task.js │ │ │ ├── task_info.js │ │ │ └── task_log.js │ │ └── user/ │ │ ├── user.js │ │ └── user_info.js │ └── vendors/ │ ├── bootgrid/ │ │ ├── .bower.json │ │ ├── .npmignore │ │ ├── jquery.bootgrid.css │ │ ├── jquery.bootgrid.fa.js │ │ ├── jquery.bootgrid.js │ │ └── jquery.bootgrid.updated.js │ ├── bootstrap-growl/ │ │ └── bootstrap-growl.js │ ├── bootstrap-wizard/ │ │ └── jquery.bootstrap.wizard.js │ ├── bower.json │ ├── bower_components/ │ │ ├── Waves/ │ │ │ └── 0.7.4/ │ │ │ ├── waves.css │ │ │ └── waves.js │ │ ├── animate.css/ │ │ │ └── animate.css │ │ ├── autosize/ │ │ │ └── 3.0.5/ │ │ │ └── autosize.js │ │ ├── bootstrap/ │ │ │ └── 3.3.6/ │ │ │ ├── css/ │ │ │ │ ├── bootstrap-theme.css │ │ │ │ └── bootstrap.css │ │ │ └── js/ │ │ │ ├── bootstrap.js │ │ │ └── npm.js │ │ ├── bootstrap-select/ │ │ │ └── 1.7.2/ │ │ │ ├── css/ │ │ │ │ └── bootstrap-select.css │ │ │ └── js/ │ │ │ ├── bootstrap-select.js │ │ │ └── i18n/ │ │ │ ├── defaults-bg_BG.js │ │ │ ├── defaults-cs_CZ.js │ │ │ ├── defaults-da_DK.js │ │ │ ├── defaults-de_DE.js │ │ │ ├── defaults-en_US.js │ │ │ ├── defaults-es_CL.js │ │ │ ├── defaults-eu.js │ │ │ ├── defaults-fa_IR.js │ │ │ ├── defaults-fr_FR.js │ │ │ ├── defaults-hu_HU.js │ │ │ ├── defaults-it_IT.js │ │ │ ├── defaults-ko_KR.js │ │ │ ├── defaults-nl_NL.js │ │ │ ├── defaults-pl_PL.js │ │ │ ├── defaults-pt_BR.js │ │ │ ├── defaults-pt_PT.js │ │ │ ├── defaults-ro_RO.js │ │ │ ├── defaults-ru_RU.js │ │ │ ├── defaults-sk_SK.js │ │ │ ├── defaults-sl_SI.js │ │ │ ├── defaults-sv_SE.js │ │ │ ├── defaults-tr_TR.js │ │ │ ├── defaults-ua_UA.js │ │ │ ├── defaults-zh_CN.js │ │ │ └── defaults-zh_TW.js │ │ ├── bootstrap-sweetalert/ │ │ │ └── lib/ │ │ │ ├── sweet-alert-animations.less │ │ │ ├── sweet-alert-combine.less │ │ │ ├── sweet-alert.css │ │ │ ├── sweet-alert.js │ │ │ └── sweet-alert.less │ │ ├── eonasdan-bootstrap-datetimepicker/ │ │ │ └── 4.7.14/ │ │ │ └── css/ │ │ │ └── bootstrap-datetimepicker.css │ │ ├── flot/ │ │ │ ├── jquery.flot.js │ │ │ └── jquery.flot.resize.js │ │ ├── flot-orderBars/ │ │ │ ├── .bower.json │ │ │ ├── README.md │ │ │ └── js/ │ │ │ └── jquery.flot.orderBars.js │ │ ├── flot.curvedlines/ │ │ │ └── curvedLines.js │ │ ├── jquery/ │ │ │ └── 2.1.4/ │ │ │ └── jquery.js │ │ ├── jquery.easy-pie-chart/ │ │ │ └── 2.1.6/ │ │ │ └── jquery.easypiechart.js │ │ ├── malihu-custom-scrollbar-plugin/ │ │ │ ├── jquery.mCustomScrollbar.css │ │ │ └── jquery.mCustomScrollbar.js │ │ ├── material-design-iconic-font/ │ │ │ └── dist/ │ │ │ └── css/ │ │ │ └── material-design-iconic-font.css │ │ ├── moment/ │ │ │ └── 2.10.6/ │ │ │ └── moment-with-locales.js │ │ ├── nouislider/ │ │ │ └── distribute/ │ │ │ ├── jquery.nouislider.all.js │ │ │ └── jquery.nouislider.js │ │ └── summernote/ │ │ └── dist/ │ │ ├── summernote-bs3.css │ │ ├── summernote.css │ │ └── summernote.js │ ├── farbtastic/ │ │ ├── farbtastic.css │ │ └── farbtastic.js │ ├── fileinput/ │ │ ├── fileinput.js │ │ └── fileinput.less │ ├── input-mask/ │ │ └── input-mask.js │ ├── sparklines/ │ │ └── jquery.sparkline.js │ └── summernote/ │ ├── .bower.json │ └── dist/ │ ├── summernote-bs3.css │ ├── summernote-updated.js │ ├── summernote.css │ └── summernote.js └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ **/target .idea *.iml log *.log doc/.vuepress/dist doc/node_modules 备忘.md .classpath .factorypath .vscode .settings .project .DS_Store .settings .project launch.json website ================================================ FILE: CHANGELOG.md ================================================ # Change Log ## latest - Fix 业务日志中如果获取不到字段值对应的中文名称则返回字段名本身,代替之前的null - Issue IN查询增加使用数组作为参数 - Change 调整按钮样式 - Issue BaseService增加缓存支持 - Issue BaseService添加count方法 - Issue 添加实现性质功能:使用leaflet提供gis服务 - Issue 添加实验性质功能:使用actuator对应用进行监控 - Issue 针对ajax-object.js返回的异常信息统一处理 - Issue 代码生成功能针对列表页面生成根据字段排序功能 - Issue service层封装根据sql和条件查询数据列表功能 ## Fixes - Fix 更新缓存的时候连带更新常量工具类中使用的本地(TimeCacheMap)缓存 ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019-present enilu.cn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ English | [简体中文](./README.zh-CN.md) ## Introduction [materail-admin](https://github.com/enilu/material-admin) is a Materail Design Admin Framework based on [Spring Boot2](https://spring.io/projects/spring-boot/) and [Bootstrap](https://www.bootcss.com/). It includes basic functions commonly used in the background of rights management, configuration management, organization, users, scheduled tasks, and message management. Refining a typical business model can help you quickly build an enterprise back-end product system. - [Online Demo](http://material.enilu.cn) - Separate versions of the front and rear ends are also provided.[web-flash](http://enilu.github.com/web-flash) ## Preparation - Download JAVA IDE :Eclipse or Intellij IDEA - Install the Lombook plugin in the IDE. - Install JDK1.8 ,MySQL5.5+,Maven **Welcome to make an issue or pr** ## Features - Department management - Account management - Role management - Menu management - Permission managemenet - Configuration - Dict managemenet - Schedule - log ## Getting started ``` # create dabase in mysql: CREATE DATABASE IF NOT EXISTS material DEFAULT CHARSET utf8 COLLATE utf8_general_ci; CREATE USER 'material'@'%' IDENTIFIED BY 'material123'; GRANT ALL privileges ON material.* TO 'material'@'%'; flush privileges; # clone the project git clone https://github.com/enilu/material-admin.git # enter the project directory cd material-admin # build and package mvn package # run the project java -jar target/material.jar # open the browser and enter: http://localhost:8085 user/password:admin/admin ``` ## Online Demo [http://material.enilu.cn](http://material.enilu.cn) ## 交流 - Bugs: [Issues](https://github.com/enilu/material-admin/issues/new) - QQ:752844606 - wechat:myenilu - Gitter: [Gitter channel](https://gitter.im/springboot-material-admin/community) ## License [MIT](https://github.com/enilu/material-admin/blob/master/LICENSE) Copyright (c) 2017-present enilu ================================================ FILE: README.zh-CN.md ================================================ [English](./README.md) | 简体中文 ## 简介 [material-admin](https://gitee.com/enilu/material-admin) 是一个通用的基础的后台管理系统,它基于[Spring Boot2](https://spring.io/projects/spring-boot/) 和 [Bootstrap](https://www.bootcss.com/)实现。它使用了当下流行的java 框架Spring Boot和基于Material Design风格的组件构建。内置了权限管理,配置管理,组织机构,用户,定时任务,消息管理等后台常用的基础功能。提炼了典型的业务模型,可以帮助你快速搭建企业级中后台产品系统。 - [在线预览](http://material.enilu.cn) - [gitee地址](https://gitee.com/enilu/material-admin) - 另提供前后端分离版本[web-flash](http://enilu.gitee.io/web-flash) ## 准备 你需要下载JAVA IDE :Eclipse或者Intellij IDEA 你需要在开发环境中安装Lombok插件,用以生成java entity的set get方法。 你需要在本地安装JDK1.8 ,MySQL5.5+,Maven **如有问题请,欢迎 issue 和 pr** ## 技术选型 - 核心框架:spring boot - 数据库层:spring data jpa - 安全框架:Shiro - 数据库连接池:Druid - 缓存:Ehcache - 前端:Beetl模版+Bootstrap ## 功能 - 部门管理 - 用户管理 - 角色管理 - 菜单管理 - 权限分配 - 参数管理 - 数据字典 - 定时任务 - 业务日志 - 登录日志 ## 开发 - 克隆本项目 - 导入idea或者eclipse,确保开发工具安装了lombok插件,如果不了解该插件,请自行搜索 - 创建数据库: ```sql CREATE DATABASE IF NOT EXISTS material DEFAULT CHARSET utf8 COLLATE utf8_general_ci; CREATE USER 'material'@'%' IDENTIFIED BY 'material@123ABC'; GRANT ALL privileges ON material.* TO 'material'@'%'; flush privileges; ``` - 更改配置文件中相应数据库配置 - material-manage启动的时候会自动创建表并导入src/main/resources/import.sql到数据库中,无需开发手动初始化表结构 - 启动material-manage中的类:cn.enilu.material.admin.AdminApplication - 访问 http://localhost:8085, - 登录,用户名密码:admin/admin ## Online Demo [在线 Demo](http://material.enilu.cn) ## 文档 [https://enilu.gitee.io/material-admin](https://enilu.gitee.io/material-admin) ## 交流 - 关注公众号:嗨客帝国,点击web-flash菜单进群交流。 ![公众号二维码](doc/img/haike.jpg) - 论坛提问 [http://bbs.enilu.cn](http://bbs.enilu.cn) - Gitter: [Gitter channel](https://gitter.im/springboot-material-admin/community) ## License [MIT](https://github.com/enilu/material-admin/blob/master/LICENSE) Copyright (c) 2017-present enilu ================================================ FILE: doc/.vuepress/config.js ================================================ module.exports = { title: 'Material Admin', description: '使用Material Admin快速构建web应用程序', base: '/material-admin/', head: [ ['link', { rel: 'shortcut icon', type: "image/x-icon", href: './favicon.ico' }] ], evergreen: true, editLinkText:'在 GitHub 上编辑此页', port: 8081, ga: 'UA-71886989-13', themeConfig: { repo: 'enilu/material-admin', docsDir: 'docs', editLinks: true, editLinkText: '编辑此页面!', nav: [ {text: '文档', link: '/'}, {text: '公告', link: 'https://www.oschina.net/p/material-admin'}, // {text: '捐赠',link:'/donate'}, {text: '资源',link:'/resource'}, {text: '周边', items:[ {text: '代码生成',link:'/ecosystem/code-generator'}, {text: '数据库文档生成',link:'/ecosystem/database-doc-generator'}, ] }, {text: '码云',link:'https://gitee.com/enilu/material-admin'}, ], sidebar: [ { title: '基本准备', collapsable: false, children: [ '/base/jdkAndMaven', '/base/modules' ] }, { title: '10分钟将本项目跑起来', collapsable: false, children: [ '/quickstart/quickstart', '/quickstart/clone', '/quickstart/initDb', '/quickstart/config', '/quickstart/startup' ] }, { title: '开发第一个功能', collapsable: false, children: [ '/helloworld/crud', '/helloworld/create_table', '/helloworld/base', '/helloworld/list', '/helloworld/add', '/helloworld/delete', '/helloworld/update', '/helloworld/menuAndPermission' ] }, { title: '基本功能介绍', collapsable: false, children: [ '/feature/modules', '/feature/menu', '/feature/dict', '/feature/log', '/feature/monitor' ] },{ title: '进阶', collapsable: false, children: [ '/action/sqlite', '/action/cache', '/action/task', '/action/jpaauditing.md' ] },{ title: '其他', collapsable: false, children:[ '/other/faq' ] } ] } } ================================================ FILE: doc/README.md ================================================ --- home: true heroImage: /logo.png actionText: 快速开始 → actionLink: /base/preface footer: MIT Licensed | Copyright © 2018-present enilu ---

简单快捷

基于spring boot快速构建web应用程序,开发,部署,维护方便快捷。

功能完善

封装完善的后台管理功能,包括用户、部门、权限、日志、字典、等基础功能。

现代化界面

使用Bootstrap和Material Design风格的前端框架做展示,拥有良好的使用体验。

最新技术栈

使用spring boot+jpa构建后端服务,使用Beetl+Bootstrap构建页面。

成熟方案

基于该系统已经上线了很多大大小小的后管系统,方案成熟,坑少。

文档完善

完善的文档和使用手册,帮助初级用户也可以快速构建自己的系统。

================================================ FILE: doc/action/cache.md ================================================ # 缓存的应用 对缓存的应用几乎成了系统的标配,material-admin中也有对缓存的应用。 本章节介绍系统中缓存的设计和用法 ## 底层缓存支持 - material-admin为了给上层应用提供缓存支持,提供了CacheDao接口,CacheDao接口负责和底层的缓存组件打交道,比如Ehcache、Redis、ssdb,甚至自己实现的缓存系统皆可。 - 在material-admin中针对CacheDao的默认实现类是EhcacheDao,没错material-admin默认使用的缓存组件就是Ehcache。 - 针对ehcache的具体用法实现,可以查看EhcacheDao的实现和ehcache.xml配置,这里不再赘述。 ## 缓存应用 目前material-admin中有两种形式的缓存应用方式: - 手动管理缓存,比如针对系统参数和字典数据的管理维护,具体用法也很简单,分为以下几个步骤 - 系统启动的时候通过CacheListener将数据加载到缓存 - 具体的功能中使用的时候注入对应的缓存类使用即可。 - 数据更新的时候重新刷新缓存 - 通过注解自动维护缓存。 - 主要在service中通过@Cacheable 和 @CacheEvict 注解来维护缓存,具体可参考BaseService中的用法 下面是一些用法的关键代码: - CacheListner 缓存监听器,启动的时候将数据从数据库加载到缓存中 ```java @Component public class CacheListener implements CommandLineRunner { @Autowired private ConfigCache configCache; @Autowired private DictCache dictCache; public void loadCache() { configCache.cache(); dictCache.cache(); } @Override public void run(String... strings) throws Exception { Thread thread = new Thread(new Runnable() { @Override public void run() { loadCache(); } }); thread.start(); } } ``` - Cache顶级缓存接口,定义了缓存基本的三个方法 ```java public interface Cache { void cache(); Object get(String key); void set(String key, Object val); } ``` - Service中的缓存的使用 ```java @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#id") public void delete(ID id) { dao.deleteById(id); } @Cacheable(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#id") public T get(ID id) { return dao.findById(id).get(); } ``` ## 备注 **为什么选用Ehcahce** - 目前最流行的缓存中间件非Redis莫属。而且我司大多数产品和项目也是使用redis,但是考虑到Ehcahe的开箱即用(直接整合到项目中,不需要部署专门的缓存服务),所以在material-admin 默认支持Ehcache, - 想用Redis也很简单,参考EhcacheDao实现一个RedisCacheDao即可。 **当数据库中数据变化的时候缓存中的数据如何做到更新** - material-admin中的做法简单明了,目前针对全局参数Cfg和字典Dict表的进行更新操作的时候分别调用ConfigCache和DictCache的cache()重新将数据库中的数据加载到缓存中. - 也可以通过@CacheEvict注解来刷新缓存 - 具体生产中,也许会有很多产品都是微服务架构,这时候推荐使用zookeeper来做配置变更的管理,感兴趣的同学可以发issue或者在qq群中讨论,本文档不再赘述 ================================================ FILE: doc/action/jpaauditing.md ================================================ # 使用注解自动保存数据的维护时间和修改者 通常来说,我们都有这样的需求:我需要知道库中的数据是由谁创建,什么时候创建,最后一次修改时间是什么时候,最后一次修改人是谁。 在Spring jpa中可以通过在实体bean的属性或者方法上添加以下注解来实现上述需求@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy。 - @CreatedDate 表示该字段为创建时间时间字段,在这个实体被insert的时候,会设置值 - @CreatedBy 表示该字段为创建人,在这个实体被insert的时候,会设置值 - @LastModifiedDate 最后修改时间 实体被update的时候会设置 - @LastModifiedBy 最后修改人 实体被update的时候会设置 ## 使用方式 ### 实体类添加注解 - 首先在实体中对应的字段加上上述4个注解 - 在material-admin中我们提取了一个基础实体类BaseEntity,并将对应的字段添加上述4个注解,所有需要记录维护信息的表对应的实体都集成该类 ```java import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.io.Serializable; import java.util.Date; @MappedSuperclass @Data public abstract class BaseEntity implements Serializable { @Id @GeneratedValue private Long id; @CreatedDate @Column(name = "create_time",columnDefinition="DATETIME COMMENT '创建时间/注册时间'") private Date createTime; @Column(name = "create_by",columnDefinition="bigint COMMENT '创建人'") @CreatedBy private Long createBy; @LastModifiedDate @Column(name = "modify_time",columnDefinition="DATETIME COMMENT '最后更新时间'") private Date modifyTime; @LastModifiedBy @Column(name = "modify_by",columnDefinition="bigint COMMENT '最后更新人'") private Long modifyBy; } ``` ### 实现AuditorAware接口返回操作人员的id 配置完上述4个注解后,在jpa.save方法被调用的时候,时间字段会自动设置并插入数据库,但是CreatedBy和LastModifiedBy并没有赋值 这两个信息需要实现AuditorAware接口来返回操作人员的id - 首先需要在项目启动类添加@EnableJpaAuditing 注解来启用审计功能 ```java @SpringBootApplication @EnableJpaAuditing public class AdminApplication extends WebMvcConfigurerAdapter { //省略 } ``` - 然后实现AuditorAware接口返回操作人员的id ```java @Configuration public class UserIDAuditorConfig implements AuditorAware { @Override public Optional getCurrentAuditor() { ShiroUser shiroUser = ShiroKit.getUser(); if(shiroUser!=null){ return Optional.of(shiroUser.getId()); } return Optional.of(Constants.SYSTEM_USER_ID); } } ``` ================================================ FILE: doc/action/sqlite.md ================================================ # 将数据库切换为sqlite sqlite和mysql兼容性比较好,使用master分支做很小的调整就可以支持sqlite。 下面介绍如何将master分支经过简单调整将底层数据库从mysql切换为sqlite。 ## 数据库替换 这节描述如何将mysql替换为sqlite。 在本系统中使用的mysql建表语句中,只需要下面字段类型调整下就可以用在sqlite中。 - 将bigint类型替换为INTEGER - 将verchar类型替换为TEXT - 将date、datetime类型皆替换为TEXT或INTEGER,我在我司的产品中都是替换为text类型,这样也不用考虑日期格式化的问题。 将更改过的语句在mysql中执行即可。 ## entity模块调整 - 将实体类中所有日期类型的字段替换为String,当然你也可以直接继续用Date(这样的话,sqlite的对应字段类型必须为INTGER) 没错,只需要调整日期类型的属性即可,甚至也不需要调整(如果数据库中对应字段使用INTEGER的话) ## 业务代码调整 如果在上一小节,你将日期类型调整为String,那么在涉及到所有为entity设置日期属性的时候用DateUtil.getTime()代替new Date()方法即可, DateUtil.getTime() 方法会返回当前日期的yyyy-MM-dd HH:mm:ss格式 大功告成,是不是很简单! ## 使用sqlite常见问题 ### No Dialect mapping for JDBC type: 0 ``` org.springframework.orm.jpa.JpaSystemException: No Dialect mapping for JDBC type: 0; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 0 ``` 出现这个问题的原因是: **返回值返回类型为Null,hibernate不支持SQLite的空值类型,所以需要我们自定义映射关系** - 解决方法 - 自定义SQLiteDialect ```java package cn.enilu.material.config; import java.sql.Types; public class SQLiteDialect extends com.enigmabridge.hibernate.dialect.SQLiteDialect { public SQLiteDialect(){ super(); registerHibernateType(Types.NULL,"null"); } } ``` - 配置该方言 ```properties spring.jpa.properties.hibernate.dialect=cn.enilu.material.config.SQLiteDialect ``` **参考资料:** - https://kevin12.iteye.com/blog/1768258 - https://stackoverflow.com/questions/10719117/sqlite-no-dialect-mapping-for-jdbc-type-0-hibernate ================================================ FILE: doc/action/task.md ================================================ # 定时任务管理 这两年做的项目和产品几乎多有定时任务管理的需求。 常用的场景有: - 定时给用户发送一些消息 - 定时进行一些报表的计算 - 定时去指定的接口get一些数据 - 定时降一些报表发送到指定的邮箱 当然还有很多,没有必要一一列举 ## 需求 定时任务的需求通常是这样的 - 1 需要添加一个定时任务,做一些事情。但是什么时候做要我自己配置,而且还想配置一些参数进去,比如我想定时给指定的email发送邮件. - 2 可以临时禁用一个任务。 - 3 看定时任务执行的历史日志。 ## 具体用法 material-admin对定时任务管理功能的实现如下。 ## 表结构 material-admin提供了两个表:t_sys_task(任务)和t_sys_task_log(任务执行日志) - t_sys_task ```sql CREATE TABLE `t_sys_task` ( `id` bigint(64) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `name` varchar(50) DEFAULT NULL COMMENT '任务名', `job_group` varchar(50) DEFAULT NULL COMMENT '任务组', `job_class` varchar(255) DEFAULT NULL COMMENT '执行类', `note` varchar(255) DEFAULT NULL COMMENT '任务说明', `cron` varchar(50) DEFAULT NULL COMMENT '定时规则', `data` text COMMENT '执行参数', `exec_at` datetime DEFAULT NULL COMMENT '执行时间', `exec_result` text COMMENT '执行结果', `disabled` tinyint(1) DEFAULT NULL COMMENT '是否禁用', `createtime` datetime DEFAULT NULL, `creator` bigint(20) DEFAULT NULL, `concurrent` tinyint(4) DEFAULT '0' COMMENT '是否允许并发', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` - t_sys_task_log ```sql CREATE TABLE `t_sys_task_log` ( `id` bigint(64) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `name` varchar(50) DEFAULT NULL COMMENT '任务名', `exec_at` datetime DEFAULT NULL COMMENT '执行时间', `exec_success` int(11) DEFAULT NULL COMMENT '执行结果(成功:1、失败:0)', `job_exception` varchar(255) DEFAULT NULL COMMENT '抛出异常', `id_task` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` ### 创建一个定时任务类 比如我创建一个测试任务类 ```java package cn.enilu.material.service.task.job; import cn.enilu.material.service.task.JobExecuter; import com.alibaba.fastjson.JSON; import org.springframework.stereotype.Component; @Component public class HelloJob extends JobExecuter { @Override public void execute(Map dataMap) throws Exception { System.out.println("输出配置参数如下 :"+JSON.toJSONString(dataMap)); System.out.println("这里可以编写任意其他业务逻辑"); } } ``` ### 页面配置一个定时任务 在任务管理页面可以增上改查一个定时任务,还可以禁用、启用定时任务 - 添加定时任务 ![task-add](../img/action/task_add.png) - 定时任务列表 ![task-list](../img/action/task_list.png) - 查看定时任务执行历史日志 ![task-log](../img/action/task_log.png) ================================================ FILE: doc/base/jdkAndMaven.md ================================================ # 开发前必读 本章介绍本书所需要的一些准备工作. 请确保把各部分的准备工作完成 本文档基于Intellij IDEA,Mysql,Maven,JDK8这四个基本工具, 当然你也可以用Eclipse开发工具。 **Jdk** - 请选用当前最新的版本,根据平台选用X64或X86版本的JDK8,并妥善安装 **MySQL** - [Mysql官网](https://dev.mysql.com/) - [下载页面](https://dev.mysql.com/downloads/), 选用5.6以上系列的版本 - 妥善安装,并配置账号密码 **Intellij IDEA** - [Intellij IDEA官网](https://www.jetbrains.com/idea/) - [下载页面](https://www.jetbrains.com/idea/download/) - 请自行下载安装合适版本的IDEA(或者eclipse) **Lombok** - Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。 - 本系统用通过给实体类增加@Data注解让给实体类自动生成setter,getter方法。 - 开发之前需要安装开发工具对应的Lombok插件 - 添加新的实体的时候要再类名上增加@Data注解。 **Maven** -[下载页面](http://maven.apache.org/download.cgi) ================================================ FILE: doc/base/modules.md ================================================ # 基本包结构 本节详细说明本项目的基本目录结构 ## material-admin模块 material-admin包含3个核心模块: - material-core 项目核心模块,包括实体层,dao层,service层,以及各种工具类 - material-generator 代码生成模块,配合IDEA Intellij的代码生成插件:webflash-generator可以提高开发效率 - material-manage 项目web模块,包含项目页面内容,controller层 其中material-manage是一个java web模块 其他都为java se模块, ================================================ FILE: doc/base/preface.md ================================================ # 前言 本文档以向导的方式引导用户使用material-admin系统做二次开发, material-admin项目本身有readme文件,如果你有使用spring boot的和beetl的经验,那么基本上你是用不上本文档了, 有什么问题直接看代码即可。大多数功能都可以参考代码,即使找不到的google和百度也能帮到你。 但是考虑到有的开发者可能初次使用上述组件,有的甚至刚接触java不久,那么本文当将引导你一步步使用本系统搭建一个后台管理系统,并做二次开发。 ## 简介 [materail-admin](https://github.com/enilu/material-admin) 是一个通用的基础的后台管理系统,它基于[Spring Boot2](https://spring.io/projects/spring-boot/) 和 [Bootstrap](https://www.bootcss.com/)实现。它使用了当下流行的java 框架Spring Boot和基于Materail Design风格的组件构建。内置了权限管理,配置管理,组织机构,用户,定时任务,消息管理等后台常用的基础功能。提炼了典型的业务模型,可以帮助你快速搭建企业级中后台产品系统。 - [在线预览](http://material.enilu.cn) - [gitee地址](https://gitee.com/enilu/material-admin) ## 准备 你需要下载JAVA IDE :Eclipse或者Intellij IDEA 你需要在开发环境中安装Lombook插件,用以生成java entity的set get方法。 你需要在本地安装JDK1.8 ,MySQL5.5+,Maven **如有问题请,欢迎 issue 和 pr** ## 技术选型 - 核心框架:spring boot - 数据库层:spring data jpa - 安全框架:Shiro - 数据库连接池:Druid - 缓存:Ehcache - 前端:Beetl模版+Bootstrap ## 功能 - 部门管理 - 用户管理 - 角色管理 - 菜单管理 - 权限分配 - 参数管理 - 数据字典 - 定时任务 - 业务日志 - 登录日志 ## 开发 - 克隆本项目 - 导入idea或者eclipse,确保开发工具安装了lombok插件,如果不了解该插件,请自行搜索 - 创建数据库: ```sql CREATE DATABASE IF NOT EXISTS material DEFAULT CHARSET utf8 COLLATE utf8_general_ci; CREATE USER 'material'@'%' IDENTIFIED BY 'material123'; GRANT ALL privileges ON material.* TO 'material'@'%'; flush privileges; ``` - 更改配置文件中相应数据库配置 - material-manage启动的时候会自动创建表并导入src/main/resources/import.sql到数据库中,无需开发手动初始化表结构 - 启动material-manage中的类:cn.enilu.material.admin.AdminApplication - 访问 http://localhost:8085, - 登录,用户名密码:admin/admin ## Online Demo [在线 Demo](http://material.enilu.cn) ## License [MIT](https://github.com/enilu/material-admin/blob/master/LICENSE) Copyright (c) 2017-present enilu ## 交流 - Bugs: [Issues](https://github.com/enilu/material-admin/issues/new) - QQ: 欢迎加入qq交流群 752844606 - 微信群: 请添加myenilu好友后入群,添加备注:material-admin - Gitter: [Gitter channel](https://gitter.im/material-admin/community) ================================================ FILE: doc/config/application.md ================================================ # application配置 完善中... ================================================ FILE: doc/config/beetl.md ================================================ # beetl模板配置 完善中... ================================================ FILE: doc/config/ehcache.md ================================================ # ehcache缓存配置 完善中... ================================================ FILE: doc/config/logback.md ================================================ # 日志输出配置 完善中... ================================================ FILE: doc/config/shiro.md ================================================ # shiro权限配置 完善中... ================================================ FILE: doc/config/swagger.md ================================================ # swagger在线文档配置 完善中... ================================================ FILE: doc/donate.md ================================================ ::: tip Donate 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 ::: ================================================ FILE: doc/ecosystem/code-generator.md ================================================ # 代码生成 ## 用法 - 在material-core/pom.xml中添加依赖 ```xml cn.enilu material-generator ${project.version} provided ``` - 下载intellij代码生成插件,在插件中心搜索并安装插件:webflash-generator - 写好实体类,例如: ```java package cn.enilu.material.bean.entity.test; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; @Entity(name="t_test_boy") @Table(appliesTo = "t_test_boy",comment = "男孩") @Data public class Boy extends BaseEntity { @Column(columnDefinition = "VARCHAR(32) COMMENT '姓名'") private String name; @Column(columnDefinition = "INT COMMENT '年龄'") private Integer age; @Column(columnDefinition = "VARCHAR(12) COMMENT '生日'") private String birthday; @Column(name = "has_girl_friend",columnDefinition = "TINYINT COMMENT '是否有女朋友'") private Boolean hasGirFriend; } ``` - 上面实体类注意事项 - @Table注解要使用org.hibernate.annotations.Table 不要使用javax.persistence.Table - @Table注解 必须配置表名(applyiesTo)和注释(comment) - @Column注解必须配置columnDefinition来表述列信息(英文全部大写):包括类型,注释COMMENT - 实体类必须继承BaseEntity - 实体类准备好了后,打开实体类,右键点击“Generator"-->"web-flash-mvc",弹出如下图所示对话框 ![code-generator](./doc/code-generate.jpg) **注意**不用更改对话框中的配置(大部分没有什么作用) - 运行生成代码后,将会生成controller,service,repository,以及对应的增上改查页面和js,以TestBoy为例,生成的代码如下所示: ![generate-result](./doc/generate-result.png) - 代码生成后,在系统中配置对应的菜单和权限,即可使用 ![菜单配置](./doc/menu.png) ![权限配置](./doc/role.png) ![功能预览](./doc/boy-list.png) ================================================ FILE: doc/ecosystem/database-doc-generator.md ================================================ # 数据库文档生成器 - 这是一个简单的小工具,可以根据数据库表结构生成数据库设计文档(格式包括markdown,html,word),支持数据库(MySQL,Oracle,PostgreSQL) - 如果你嫌PowerDesigner太重,那么可以试试该工具。 - 你可以下载[release](https://github.com/enilu/database-doc-generator/releases/tag/1.0)包来或者下载[源代码](https://github.com/enilu/database-doc-generator)来使用。 - 如果使用源代码,需要先克隆该项目后运行mvn package打包,然后运行发布包中的bin/start.bat - 运行程序后按照下面提示输入对应数据库参数: ```bash choose database: 1:mysql 2:oracle 3:PostgreSQL Select the appropriate numbers choose database type (Enter 'c' to cancel): 3 input database name: gunslite input host: localhost input port: 5432 input username: enilu input password: 123456 ``` - 输入完成后回车,即可生成数据库文档目录${dbname}-doc,目录中文档以markdown文件为载体: ![doc](./doc/doc.jpg) - 确保安装了gitbook后,进入上述文件目录的命令行窗口运行:gitbook serve ```bash E:\\database-doc-generator-20181006100721\material-admin-doc>gitbook serve openssl config failed: error:02001003:system library:fopen:No such process Live reload server started on port: 35729 Press CTRL+C to quit ... info: 7 plugins are installed info: loading plugin "livereload"... OK info: loading plugin "highlight"... OK info: loading plugin "search"... OK info: loading plugin "lunr"... OK info: loading plugin "sharing"... OK info: loading plugin "fontsettings"... OK info: loading plugin "theme-default"... OK info: found 15 pages info: found 0 asset files info: >> generation finished with success in 1.6s ! Starting server ... Serving book on http://localhost:4000 ``` - 访问 http://localhost:4000,即可在线查看数据库文档 ![summary](./doc/summary.jpg) ![table](./doc/table.jpg) - 另外还可以生成word文档哦;虽然有点简陋: ![word](./doc/word.jpg) ================================================ FILE: doc/feature/dict.md ================================================ # 字典管理 该模块提供了对各种枚举数据进行维护的功能。 ![dict](./img/dict.jpg) 后台中常量工厂ConstantFactory封装了的对字典的常规功能。 ## 根据名称获取其所有字典列表 使用场景,比如页面查询表单中需要一个联系人关系的下拉框来所搜索,具体用法为: ```html <#select id="account" name="我方账户" > @for(dict in constant.getDicts('学历类型')){ @} ``` ## 根据字典id获取字典名称 数据库中存的是字典id,但是页面展示需要具体的值 ```java String degreeName = ConstantFactory.me().findByPnameAndCode("学历类型",1).getName(); model.addAttribute('degreeName',degreeName); ``` ================================================ FILE: doc/feature/log.md ================================================ # 日志管理 日志管理包括两方面: 一个是后台用户登录日志的查看 一个是业务日志查看,业务日志内容主要包含两方面:系统产生的异常和用户操作日志。 ## 登录日志 用户登录系统和退出系统的时候会调用LogTaskFactory记录相关日志,并在“登录日志”页面进行展示。 ![loginLog](./img/loginLog.jpg) ## 业务日志 业务日志包含异常日志和业务操作日志两大类日志收集、保存和展示。 ### 异常日志 系统提供了异常处理类:GlobalExceptionHandler 来对系统各种异常进行统一收集保存。 该类提供了九个方法来分别对9中常见的异常类型进行收集保存,如果开发者自己有特殊需求需要对其他异常类型处理。,可以通过新增处理方式来对新的异常进行收集。 ### 业务操作日志 系统提供了通过注解的形式可以方便的添加业务操作日志,比如在新增部门的是增加业务日志通过如下方式: 在DeptController的新增部门方法增加注解: ```java /** * 新增部门 */ @BussinessLog(value = "添加部门", key = "simplename", dict = DeptDict.class) @RequestMapping(value = "/add") @Permission @ResponseBody public Object add(Dept dept) { if (ToolUtil.isOneEmpty(dept, dept.getSimplename())) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //完善pids,根据pid拿到pid的pids deptSetPids(dept); return this.deptRepository.save(dept); } ``` 具体的实现逻辑感兴趣的同学可以自行通过注解类:BusinessLog进行跟进查看。 ================================================ FILE: doc/feature/menu.md ================================================ # 菜单管理 菜单管理包含两部分:一个是左侧菜单树的维护,一个是页面操作功能(主要是按钮)的维护。 不管是左侧菜单还是按钮,都需要在角色中进行配置才能正常显示出来。 先看下菜单列表大致了解下菜单管理都维护哪些内容: ![menu](./img/menu.jpg) ## 左侧菜单维护 - 菜单维护基本上采用了两级菜单形式:如上图所示:第一级菜单为“系统管理”,“系统管理”中包含了“用户管理”、“角色管理”等多个二级菜单。 - 其中“系统管理”为虚拟菜单,点击“系统管理”并不会跳转到真实的页面而是展开其子菜单列表;所以虚拟菜单的请求地址应配置为“#”。 - “系统管理”下的子菜单,例如“业务日志”需要配置请求地址,比如配置为“/log”,点击“业务日志”会跳转到业务日志列表页面。 ## 页面功能维护 - 菜单维护中的另外一种形式我功能(主要是操作按钮或者页面中的链接)的维护。点击按钮虽然不需要跳转到具体的页面但是也需要配置请求地址,因为代码中需要通过该地址来判断用户是否拥有操作权限。 - 例如“业务日志”菜单中有三个子菜单:“清空日志”的请求地址配置为:"log/delLog",则页面进行控制权限的写法为: ```html @if(shiro.hasPermission("/log/delLog")){ <#button name="清空日志" icon="fa-minus" clickFun="OptLog.delLog()" space="true"/> @} ``` 菜单管理的其他维护参数不赘述,具体作用配置试用一下即可知晓。 ================================================ FILE: doc/feature/modules.md ================================================ # 基本包结构 本节详细说明本项目的基本目录结构 ## material-admin模块 material-admin包含3个核心模块: - material-core - material-generator - material-manage 其中material-manage一个java web模块,其他都为java se项目; 具体每个包的作用通过名字即可看出包含的功能分别为:核心模块,代码生成模块,web管理模块 具体每个包里的细节不详细介绍,开发人员可以在使用过程中逐渐了解,本身代码量并不大,熟悉起来不需要花费太多时间。 这里仅详细说明下material-manage的内部结构,毕竟日常开发主要是基于该模块来做的。 ## material-manage material-manage是一个标准的java web项目 ![material-manage](./img/admin.jpg) 目录结构包含: - src/main/java java源码 - src/main/resources 配置文件 - src/webapp web页面和静态文件资源 这里介绍下material-manage的基本目录和开发流程 ### src/main/java/ 源代码 目录结构如下所示: ![material-manage](./img/src.jpg) - **common** 该package 封装了一些工具的类库,如一些注解,常量、枚举,异常等公共类 - **config** 该package 包含项目支持各种特性的相关配置。例如: - 支持swagger在线文档的配置 - EhCache缓存配置 - Session配置 - Shiro配置 - Beetl模版配置 - **core** 该package是项目的核心,包含注入数据源管理、缓存管理、模版管理、aop,监听器以及分页工具、各种工具类。 当然开发人员可以根据项目实际情况做二次调整和封装。 - **modular** 该pakcage存放和业务相关的代码。 比如目前提供了一个**system模块**,主要包含诸如:用户、角色、权限、日志等管理功能的系统管理功能。 system包中除了controller包是必须的,其他包都是根据具体情况选择是否需要。 ### src/webapp web页面和静态文件资源 目录结构如下所示: ![material-manage](./img/web.jpg) - static 目录为静态资源 - css、fonts,img,js分别为公共的样式、字体,图片,js资源 - modular 目录为业务用js资源,比如system即为admin内置功能的js资源,其中每个功能使用一个目录和WEB-INF/view/中的目录一一对应 ![material-web-js](./img/web-js.jpg) - WEB-INF/view 为页面目录 - common 为公共的页面框架和封装的标签目录 - 其他目录为业务页面目录,比如system即为内置的功能页面包括用户、角色、权限等管理功能的页面 ![material-web-page](./img/web-page.jpg) ================================================ FILE: doc/feature/monitor.md ================================================ # 监控管理 这里的监控功能用的是alibaba druid自带的监控功能 ![monitor](./img/monitor.jpg) ================================================ FILE: doc/helloworld/add.md ================================================ # add ## 添加参数 添加参数功能为点击“添加”按钮后调用对应的js代码逻辑弹出添加页面,在添加页面输入相关信息提交保存,保存成功后关闭弹窗,并刷新参数列表数据. ### 添加按钮注册点击函数: ```html @if(shiro.hasPermission("/cfg/add")){ <#button name="添加" icon="fa-plus" clickFun="Cfg.openAddCfg()" space="true"/> @} ``` ### 添加 按钮点击逻辑: ```javascript /** * 点击添加系统参数 */ Cfg.openAddCfg = function () { var index = layer.open({ type: 2, title: '添加系统参数', area: ['65%', '280px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/cfg/cfg_add' }); this.layerIndex = index; }; ``` ### 添加页面代码 ```html @layout("/common/include.html"){
<#input id="cfgName" name="参数名"/> <#input id="cfgValue" name="参数值" underline="true"/>
<#input id="cfgDesc" name="参数描述" underline="true"/>
<#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="CfgInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="CfgInfoDlg.close()"/>
@} ``` ### 点击“提交”按钮提交参数保存逻辑: _cfg_info.js_: ```javascript /** * 提交添加 */ CfgInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/cfg/add", function(data){ Feng.success("添加成功!"); window.parent.Cfg.table.refresh(); CfgInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.cfgInfoData); ajax.start(); } ``` ## 后台保存逻辑 ```java /** * 跳转到添加参数页面 */ @RequestMapping(value = "/cfg_add",method = RequestMethod.GET) public String add() { return PREFIX + "cfg_add.html"; } /** * 新增参数 */ @RequestMapping(value = "/add",method = RequestMethod.POST) @ResponseBody @BussinessLog(value = "添加参数", key = "cfgName",dict = CfgDict.class) public Object add(@Valid Cfg cfg) { cfgService.saveOrUpdate(cfg); return SUCCESS_TIP; } ``` ================================================ FILE: doc/helloworld/base.md ================================================ # 基础代码 ## 实体Entity ```java package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.validation.constraints.NotBlank; @Entity(name="t_sys_cfg") @Table(appliesTo = "t_sys_cfg",comment = "系统参数") @Data @EntityListeners(AuditingEntityListener.class) public class Cfg extends BaseEntity { @Column(name = "cfg_name",columnDefinition = "VARCHAR(256) COMMENT '参数名'") @NotBlank(message = "参数名不能为空") private String cfgName; @Column(name = "cfg_value",columnDefinition = "VARCHAR(512) COMMENT '参数值'") @NotBlank(message = "参数值不能为空") private String cfgValue; @Column(name = "cfg_desc",columnDefinition = "TEXT COMMENT '备注'") private String cfgDesc; } ``` ## 数据库操作Repository ```java public interface CfgRepository extends BaseRepository { } ``` ## service ```java @Service public class CfgService extends BaseService { } ``` ## controller ```java @Controller @RequestMapping("/cfg") public class CfgController extends BaseController { @Autowired private CfgService cfgService; private static String PREFIX = "/system/cfg/"; /** * 跳转到参数首页 */ @RequestMapping("") public String index() { return PREFIX + "cfg.html"; } /** * 跳转到添加参数 */ @RequestMapping("/cfg_add") public String add() { return PREFIX + "cfg_add.html"; } /** * 跳转到修改参数 */ @RequestMapping("/cfg_update/{cfgId}") public String update(@PathVariable Long cfgId, Model model) { Cfg cfg = cfgService.get(cfgId); model.addAttribute("item",cfg); return PREFIX + "cfg_edit.html"; } /** * 获取参数列表 */ @RequestMapping(value = "/list") @ResponseBody public Object list(@RequestParam(required = false) String cfgName, @RequestParam(required = false) String cfgValue) { Page page = new PageFactory().defaultPage(); if(StringUtils.isNotEmpty(cfgName)){ page.addFilter(SearchFilter.build("cfgName", SearchFilter.Operator.LIKE, cfgName)); } if(StringUtils.isNotEmpty(cfgValue)){ page.addFilter(SearchFilter.build("cfgValue", SearchFilter.Operator.LIKE, cfgValue)); } page = cfgService.queryPage(page); return packForBT(page); } /** * 新增参数 */ @RequestMapping(value = "/add") @ResponseBody @BussinessLog(value = "添加参数", key = "cfgName",dict = CfgDict.class) public Object add(@Valid Cfg cfg) { cfgService.saveOrUpdate(cfg); return SUCCESS_TIP; } /** * 删除参数 */ @RequestMapping(value = "/delete") @ResponseBody @BussinessLog(value = "删除参数", key = "cfgId",dict = CfgDict.class) public Object delete(@RequestParam Long cfgId) { cfgService.delete(cfgId); return SUCCESS_TIP; } /** * 修改参数 */ @RequestMapping(value = "/update") @ResponseBody @BussinessLog(value = "编辑参数", key = "cfgName",dict = CfgDict.class) public Object update(@Valid Cfg cfg) { cfgService.update(cfg); return SUCCESS_TIP; } /** * 参数详情 */ @RequestMapping(value = "/detail/{cfgId}") @ResponseBody public Object detail(@PathVariable("cfgId") Long cfgId) { return cfgService.get(cfgId); } ``` ================================================ FILE: doc/helloworld/create_table.md ================================================ # 建表 比如我们要开发一个系统参数的管理功能,该功能主要对系统相关参数进行增删该查。 ~~建表语句如下:~~ ```sql CREATE TABLE `t_sys_cfg` ( `id` bigint(64) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `cfg_name` varchar(100) DEFAULT NULL COMMENT '参数名', `cfg_value` varchar(3000) DEFAULT NULL COMMENT '参数值', `cfg_desc` varchar(200) DEFAULT NULL COMMENT '参数描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统参数'; ``` **注意** - 看到上面的 ~~建表语句如下:~~ 了么,知道为什么要划掉么?因为在使用本系统做开发的时候强烈建议不用自己建表,而是直接根据实体在系统启动的时候自动生成表。因为以后如果我们用代码生成工具的话,你要建个表,再写一遍实体类,不近工作做了两套,而且容易出现两别字段定义不一致的问题。 - 当然在生产环境,我个人是不建议开启Spring Boot的自动更新表结构功能的,还是自己手动建表比较稳妥。 ================================================ FILE: doc/helloworld/crud.md ================================================ # 简单的CRUD 本章将手把手带你开发一个增删该查功能,包含从建表到代码(controller,service,dao,页面),到权限控制,以及一个相对复杂的查询。 **PS** 其实真正开发中你完全可以使用代码生成工具一键生成这些功能;不过工具再好用,过程自己还是要掌握的,对吧! 接下来我们要开发一个参数管理功能,用于对系统参数进行增删该查 ## 本章知识点 * [建表](./create_table.md) * [基础代码](./base.md) * [list](./list.md) * [add](./add.md) * [delete](./delete.md) * [update](./update.md) * [添加菜单和分配权限](./menuAndPermission.md) ================================================ FILE: doc/helloworld/delete.md ================================================ # delete ## 针对要删除的数据点击行尾的删除按钮即弹出删除确认空,确认删除 _cfg.js_ ```javascript {title: '操作',formatter:function(data,row){ return ''; }} /** * 删除系统参数 */ Cfg.delete = function (id) { var operation = function() { var ajax = new $ax(Feng.ctxPath + "/cfg/delete", function (data) { Feng.success("删除成功!"); Cfg.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("cfgId", id); ajax.setType('delete'); ajax.start(); }; Feng.confirm("确认删除该记录?", operation); }; ``` ## 后台逻辑 ```java /** * 删除参数 */ @RequestMapping(value = "/delete",method = RequestMethod.DELETE) @ResponseBody @BussinessLog(value = "删除参数", key = "cfgId",dict = CfgDict.class) public Object delete(@RequestParam Long cfgId) { cfgService.delete(cfgId); return SUCCESS_TIP; } ``` ================================================ FILE: doc/helloworld/list.md ================================================ # list ## 列表页面 列表页面包含分页(根据指定条件)查询数据列表,添加,修改,删除按钮 列表页面针对:添加按钮做了权限控制,具体逻辑在下文再详细描述 ```html @layout("/common/layout.html"){

参数管理

<#table id="CfgTable"/>
@} ``` ## 查询列表数据的js代码: ```javascript /** * 系统参数管理初始化 */ var Cfg = { id: "CfgTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Cfg.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '参数名', field: 'cfgName', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, {title: '参数值', field: 'cfgValue', visible: true, align: 'center', valign: 'middle'}, {title: '参数描述', field: 'cfgDesc', visible: true, align: 'center', valign: 'middle'}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 查询系统参数列表 */ Cfg.search = function () { var queryData = {}; queryData['cfgName'] = $("#cfgName").val(); queryData['cfgValue'] = $("#cfgValue").val(); Cfg.table.refresh({query: queryData}); }; Cfg.reset = function () { $("#cfgName").val(""); $("#cfgValue").val(""); this.search(); }; $(function () { var defaultColunms = Cfg.initColumn(); var table = new BSTable(Cfg.id, "/cfg/list", defaultColunms); table.setPaginationType("server"); Cfg.table = table.init(); }); ``` ## 后台代码 **CfgController** ```java /** * 跳转到参数首页 */ @RequestMapping(value = "",method = RequestMethod.GET) public String index() { return PREFIX + "cfg.html"; } /** * 分页查询系统参数 */ @RequestMapping(value = "/list",method = RequestMethod.POST) @ResponseBody public Object list(@RequestParam(required = false) String cfgName, @RequestParam(required = false) String cfgValue) { Page page = new PageFactory().defaultPage(); if(StringUtils.isNotEmpty(cfgName)){ page.addFilter(SearchFilter.build("cfgName", SearchFilter.Operator.LIKE, cfgName)); } if(StringUtils.isNotEmpty(cfgValue)){ page.addFilter(SearchFilter.build("cfgValue", SearchFilter.Operator.LIKE, cfgValue)); } page = cfgService.queryPage(page); return packForBT(page); } ``` ================================================ FILE: doc/helloworld/menuAndPermission.md ================================================ # 添加菜单和分配权限 现在功能已经开发完毕了,但是在页面上并不能使用这个功能; 因为我没有给当前用不配置参数管理的权限。下面分两步骤启用参数管理功能 - 在菜单管理中添加参数管理的功能,包括一个菜单项(点击链接进入列表页面)和一个个功能项(新增) - 在权限管理中给指定的角色配置上述两个资源 - 在用户管理中给指定的用户配置指定的角色 ## 添加菜单项 在菜单管理中添加4条记录,添加过程中要注意一下几点: - 父级编号:选中的父级编号决定了当前功能所属哪一个模块,比如“参数管理”这一项的父级编号选择“系统管理”,则“参数管理”这一功能菜单在“系统管理”模块下。 - 是否是菜单:选择“是”,则菜单会在左侧菜单栏显示,选择“否”,则不会显示在左侧菜单栏。针对按钮功能要选择“否” - 请求地址,针对菜单选择“是”的记录,则该地址必须为页面打开的地址,针是否是菜单选择“否”的记录,则该地址必须与用作权限判断的字符串一致。 ``` 比如“添加系统参数”这一项的请求地址为“cfg/add”, 则页面判断是否有操作权限的的代码为: @if(shiro.hasPermission("/cfg/add")){ <#button name="添加" icon="fa-plus" clickFun="Cfg.openAddCfg()" space="true"/> @} ``` 添加四条菜单记录: - 参数管理 - 添加系统参数 ![menu](../img/helloworld/menu.png) ## 为角色配置菜单项 ![role](../img/helloworld/role.png) ================================================ FILE: doc/helloworld/update.md ================================================ # update 参数修改逻辑为: - 点击参数名,打开编辑页面 - 更改信息,提交修改 - 关闭修改弹窗,并刷新列表页面 ## 列表页点击要修改的参数名,弹出修改页面 ```html {title: '参数名', field: 'cfgName', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, ``` - 打开参数详情弹窗 ```javascript /** * 打开系统参数详情页 */ Cfg.openCfgDetail = function (id) { var index = layer.open({ type: 2, title: '系统参数详情', area: ['65%', '280px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/cfg/cfg_update/' + id }); this.layerIndex = index; }; ``` - 后台查询参数调准到详情逻辑 ```java /** * 跳转到修改参数 */ @RequestMapping(value = "/cfg_update/{cfgId}",method = RequestMethod.POST) public String update(@PathVariable Long cfgId, Model model) { Cfg cfg = cfgService.get(cfgId); model.addAttribute("item",cfg); return PREFIX + "cfg_edit.html"; } ``` - 参数修改页面 _cfg_edit.html_: ```html @layout("/common/include.html"){
<#input id="id" name="自增主键" value="${item.id}" disabled="disabled"/> <#input id="cfgName" name="参数名" value="${item.cfgName}" />
<#input id="cfgValue" name="参数值" value="${item.cfgValue}" underline="true"/> <#input id="cfgDesc" name="参数描述" value="${item.cfgDesc}" />
<#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="CfgInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="CfgInfoDlg.close()"/>
@} ``` ## 更改信息,提交修改 - js逻辑提交修改 ```javascript /** * 提交修改 */ CfgInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/cfg/update", function(data){ Feng.success("修改成功!"); window.parent.Cfg.table.refresh(); CfgInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.cfgInfoData); ajax.start(); } ``` - 后台逻辑提交更改 ```java /** * 修改参数 */ @RequestMapping(value = "/update",method = RequestMethod.POST) @ResponseBody @BussinessLog(value = "编辑参数", key = "cfgName",dict = CfgDict.class) public Object update(@Valid Cfg cfg) { cfgService.update(cfg); return SUCCESS_TIP; } ``` ================================================ FILE: doc/other/faq.md ================================================ # 常见问题 ## 本地开发正常,打包运行的时候提交中文内容乱码,检查了数据库编码也没问题? 打包为jar包运行的时候可以指定运行时编码为UTF8: ``` java -Dfile.encoding=utf-8 -jar xxxxxxx.jar ``` ## 使用代码生成器的时候总是报找不到Generator类或者找不到code.json配置文件 下载项目后首先mvn package 保证项目能构建并打包成功再使用代码生成器 ================================================ FILE: doc/package.json ================================================ { "scripts": { "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, "devDependencies": { "@vuepress/plugin-google-analytics": "^1.0.0-alpha.0" } } ================================================ FILE: doc/quickstart/clone.md ================================================ # 克隆本项目 本项目地址为:[https://github.com/enilu/material-admin](https://github.com/enilu/material-admin),如果对你有用,欢迎给个star 项目共两个分,支分别为: - master 项目主分支 - develop 开发分支,代码最新,但是不稳定 进入控制台输入以下命令将项目克隆到本地: git clone https://github.com/enilu/material-admin.git - 使用IDEA Intellij或者eclipse导入项目,记得开发工具要安装lombook插件哦 ================================================ FILE: doc/quickstart/config.md ================================================ # 配置项目 你已经下载项目,并且初始化好了数据库,那么接下来只需要更改相应的配置就可以运行该项目了 - 更改src/resources/application-dev.properties配置: ```properties ## 开发环境配置,修改为真实的用户名密码 spring.datasource.url=jdbc:mysql://localhost:3306/material spring.datasource.username=root spring.datasource.password=root ``` ================================================ FILE: doc/quickstart/initDb.md ================================================ # 初始化数据 本系统使用mysql数据库: - 在mysql中创建数据库 material ```sql CREATE DATABASE IF NOT EXISTS material DEFAULT CHARSET utf8 COLLATE utf8_general_ci; ``` - 启动项目系统会自动建表并初始化数据。 ================================================ FILE: doc/quickstart/quickstart.md ================================================ # 10分钟把项目跑起来 ## 真的10分钟吗? 当然,如果你的网速给力,并且依赖的软件都下载安装好了,并且手速够快,是不是前置条件有点多?^_^ ## 本章知识点 - [克隆项目](./clone.md) - [初始化数据](./initDb.md) - [配置项目](./config.md) - [启动项目](./startup.md) ================================================ FILE: doc/quickstart/startup.md ================================================ # 启动项目 - 右键直接运行 cn.enilu.material.admin.AdminApplication类即可启动material-manage后台管理系统 - 系统默认是用8085端口,参考配置文件src/resources/application.properties ```properties server.port=8085 ``` - 启动成功后访问http://localhost:8085 如下图所示 ![login](../img/quickstart/login.png) - 输入用户名/密码:admin/admin即可登录: ![index](../img/quickstart/index.png) so,是不是很简单! ================================================ FILE: doc/resource.md ================================================ # 资源 本节会整理提供一些助于开发的相关资源文档,在开发过程中学会恰当的里用这些资源将会是开发过程如虎添翼,事半功倍!。 ## 前端 ### Material Admin - [https://www.sucaihuo.com/templates/3963](https://www.sucaihuo.com/templates/3963) - 本项目使用该框架,开发过程中可以参考该框架,该框架封装很多常用的material风格的组件 ### Fontawesome 图标库 - [http://www.fontawesome.com.cn/faicons](http://www.fontawesome.com.cn/faicons) - material-manage中使用了fontawesome的图标库,如果你想用一些图标,又不知道用哪些合适,可以通过下面网站,找到合适的图标和样式名 - 比如菜单管理,在添加菜单的时候,有个图标的配置项:**图标**,这里就要求默认使用fontawesome的图标库样式。 ### Bootstrap - [https://v3.bootcss.com/css/](https://v3.bootcss.com/css/) - material-manage中使用了Bootstrap作为前端样式库,Bootstrap有丰富的样式,插件和组件,开发过程中可以参考在线文档,方便搞笑。 ### Beetl - [http://ibeetl.com/guide](http://ibeetl.com/guide) - material-manage使用Beetl做为模板引擎,Beetl是一个很好用的国产模板引擎。使用中有什么问题也可以去其官方论坛提问[http://bbs.ibeetl.com/](http://bbs.ibeetl.com/) ### 阿里巴巴矢量图库 - [https://iconfont.cn](https://iconfont.cn) - 这个站点是阿里巴巴维护的一个图标站点,里面有极其丰富的图标,如果开发过程中如果在Fontawesome和Bootstrap中的找不到合适的图标,可以来这里看看,应该不会让你失望。 ## 后端 ## Spring Boot - [https://qbgbook.gitbooks.io/spring-boot-reference-guide-zh/content/](https://qbgbook.gitbooks.io/spring-boot-reference-guide-zh/content/) 文档基于Spring Boot1.4.1的英文文档翻译 - [https://www.breakyizhan.com/springboot/3028.html](https://www.breakyizhan.com/springboot/3028.html) 文档基于Spring Boot2.0.1的英文文档翻译 - 本系统基于Spring Boot2.1.1开发,所以建议开发过程中多翻翻第二个在线文档,第一个文档可以作为蛇参考。 ================================================ FILE: doc/文档完善中 ================================================ ================================================ FILE: material-core/pom.xml ================================================ material-admin cn.enilu 0.1 4.0.0 material-core cn.enilu material-generator ${project.version} org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-mail org.quartz-scheduler quartz org.quartz-scheduler quartz-jobs com.github.qcloudsms qcloudsms org.projectlombok lombok compile org.springframework.boot spring-boot-starter-data-jpa org.springframework spring-context-support org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java com.alibaba druid-spring-boot-starter org.springframework.boot spring-boot-starter-cache org.ehcache ehcache net.sf.ehcache.internal ehcache-core com.alibaba fastjson com.google.guava guava org.springframework.boot spring-boot-starter-logging org.apache.commons commons-lang3 commons-io commons-io org.nutz nutz org.apache.shiro shiro-core org.apache.shiro shiro-spring org.apache.shiro shiro-ehcache commons-codec commons-codec org.springframework.boot spring-boot-starter-tomcat compile junit junit test org.springframework.boot spring-boot-starter-test test ================================================ FILE: material-core/src/main/java/cn/enilu/material/aop/LogAop.java ================================================ package cn.enilu.material.aop; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.dao.cache.TokenCache; import cn.enilu.material.platform.log.LogManager; import cn.enilu.material.platform.log.LogTaskFactory; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.utils.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Map; /** * 日志记录 * * @author fengshuonan * @date 2016年12月6日 下午8:48:30 */ @Aspect @Component public class LogAop { private Logger log = LoggerFactory.getLogger(this.getClass()); @Pointcut(value = "@annotation(cn.enilu.material.bean.core.BussinessLog)") public void cutService() { } @Around("cutService()") public Object recordSysLog(ProceedingJoinPoint point) throws Throwable { //先执行业务 Object result = point.proceed(); try { handle(point); } catch (Exception e) { log.error("日志记录出错!", e); } return result; } private void handle(ProceedingJoinPoint point) throws Exception { //获取拦截的方法名 Signature sig = point.getSignature(); MethodSignature msig = null; if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) sig; Object target = point.getTarget(); Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); String methodName = currentMethod.getName(); //获取用户id,admin和api模块获取idUser方式不同 Long idUser = null; HttpServletRequest request = HttpKit.getRequest(); String token = request.getHeader("Authorization"); if(StringUtils.isNotEmpty(token)) { idUser = SpringContextHolder.getBean(TokenCache.class).get(token); } if(idUser==null) { //如果当前用户未登录,不做日志 ShiroUser user = ShiroKit.getUser(); if (null == user) { return; } idUser = user.getId(); } //获取拦截方法的参数 String className = point.getTarget().getClass().getName(); Object[] params = point.getArgs(); //获取操作名称 BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class); String bussinessName = annotation.value(); String key = annotation.key(); Class dictClass = annotation.dict(); StringBuilder sb = new StringBuilder(); for (Object param : params) { sb.append(param); sb.append(" & "); } //如果涉及到修改,比对变化 String msg=""; if (bussinessName.indexOf("修改") != -1 || bussinessName.indexOf("编辑") != -1) { //todo api模块无法使用该方法获取数据 Object obj1 = LogObjectHolder.me().get(); Map obj2 = HttpKit.getRequestParameters(); try { msg = BeanUtil.contrastObj(key, obj1, obj2); }catch (Exception e){ } } else { Map parameters = HttpKit.getRequestParameters(); msg = BeanUtil.parseMutiKey(parameters); } LogManager.me().executeLog(LogTaskFactory.bussinessLog(idUser, bussinessName, className, methodName, msg)); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/aop/PermissionAop.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.aop; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.shiro.check.PermissionCheckManager; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import javax.naming.NoPermissionException; import java.lang.reflect.Method; /** * AOP 权限自定义检查 */ @Aspect @Component public class PermissionAop { @Pointcut(value = "@annotation(cn.enilu.material.bean.core.Permission)") private void cutPermission() { } @Around("cutPermission()") public Object doPermission(ProceedingJoinPoint point) throws Throwable { MethodSignature ms = (MethodSignature) point.getSignature(); Method method = ms.getMethod(); Permission permission = method.getAnnotation(Permission.class); Object[] permissions = permission.value(); if (permissions == null || permissions.length == 0) { //检查全体角色 boolean result = PermissionCheckManager.checkAll(); if (result) { return point.proceed(); } else { throw new NoPermissionException(); } } else { //检查指定角色 boolean result = PermissionCheckManager.check(permissions); if (result) { return point.proceed(); } else { throw new NoPermissionException(); } } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/Const.java ================================================ package cn.enilu.material.bean.constant; /** * 系统常量 * * @author fengshuonan * @date 2017年2月12日 下午9:42:53 */ public interface Const { long SYSTEM_USER_ID=-1; /** * 用户密码加密key */ String CRYPT_DES_KEY = "sc123456"; /** * 系统默认的管理员密码 */ String DEFAULT_PWD = "111111"; /** * 管理员角色的名字 */ String ADMIN_NAME = "administrator"; /** * 管理员id */ Integer ADMIN_ID = 1; /** * 超级管理员角色id */ Integer ADMIN_ROLE_ID = 1; /** * 接口文档的菜单名 */ String API_MENU_NAME = "接口文档"; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/cache/Cache.java ================================================ package cn.enilu.material.bean.constant.cache; /** * 所有缓存名称的集合 * * @author fengshuonan * @date 2017-04-24 21:56 */ public interface Cache { /** * 常量缓存 */ String CONSTANT = "CONSTANT"; String APPLICATION = "APPLICATION"; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/cache/CacheKey.java ================================================ package cn.enilu.material.bean.constant.cache; /** * 缓存的key集合 * * @author fengshuonan * @date 2017-04-25 9:37 */ public interface CacheKey { /** * ConstantFactory中的缓存 */ String ROLES_NAME = "roles_name_"; String SINGLE_ROLE_NAME = "single_role_name_"; String SINGLE_ROLE_TIP = "single_role_tip_"; String DEPT_NAME = "dept_name_"; String DICT_NAME = "dict_name_"; String DICT = "dict_"; String COURSE_NAME = "course_name_"; String ORG_NAME = "org_name_"; String DIC_ALL = "dic_all_"; String CFG = "cfg_"; String MENU_NAME = "menu_name_" ; String SYS_USER_NAME = "SYS_USER_NAME" ; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/factory/PageFactory.java ================================================ package cn.enilu.material.bean.constant.factory; import cn.enilu.material.bean.constant.state.Order; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.utils.StringUtils; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.utils.ToolUtil; import javax.servlet.http.HttpServletRequest; /** * BootStrap Table默认的分页参数创建 * * @author fengshuonan * @date 2017-04-05 22:25 */ public class PageFactory { public Page defaultPage() { HttpServletRequest request = HttpKit.getRequest(); //每页多少条数据 int limit = Integer.valueOf(request.getParameter("limit")); String pageNum = request.getParameter("page"); //每页的偏移量(本页当前有多少条) int offset = 0; if(StringUtils.isNotEmpty(pageNum)){ offset = (Integer.valueOf(pageNum)-1)*limit; }else { offset = Integer.valueOf(request.getParameter("offset")); } //排序字段名称 String sort = request.getParameter("sort"); //asc或desc(升序或降序) String order = request.getParameter("order"); if (ToolUtil.isEmpty(sort)) { Page page = new Page<>((offset / limit + 1), limit); page.setOpenSort(false); return page; } else { Page page = new Page<>((offset / limit + 1), limit, sort); if (Order.ASC.getDes().equals(order)) { page.setAsc(true); } else { page.setAsc(false); } return page; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/package-info.java ================================================ /** * Created on 2018/3/28 0028. * * @author enilu */ package cn.enilu.material.bean.constant; ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/BizLogType.java ================================================ package cn.enilu.material.bean.constant.state; /** * 业务日志类型 * * @author fengshuonan * @Date 2017年1月22日 下午12:14:59 */ public enum BizLogType { ALL(0, null),//全部日志 BUSSINESS(1, "业务日志"), EXCEPTION(2, "异常日志"); Integer val; String message; BizLogType(Integer val, String message) { this.val = val; this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Integer getVal() { return val; } public void setVal(Integer val) { this.val = val; } public static String valueOf(Integer value) { if (value == null) { return null; } else { for (BizLogType bizLogType : BizLogType.values()) { if (bizLogType.getVal().equals(value)) { return bizLogType.getMessage(); } } return null; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/LogSucceed.java ================================================ package cn.enilu.material.bean.constant.state; /** * 业务是否成功的日志记录 * * @author fengshuonan * @Date 2017年1月22日 下午12:14:59 */ public enum LogSucceed { SUCCESS("成功"), FAIL("失败"); String message; LogSucceed(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/LogType.java ================================================ package cn.enilu.material.bean.constant.state; /** * 日志类型 * * @author fengshuonan * @Date 2017年1月22日 下午12:14:59 */ public enum LogType { LOGIN("登录日志"), LOGIN_FAIL("登录失败日志"), EXIT("退出日志"), EXCEPTION("异常日志"), BUSSINESS("业务日志"); String message; LogType(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/ManagerStatus.java ================================================ package cn.enilu.material.bean.constant.state; /** * 管理员的状态 * * @author fengshuonan * @Date 2017年1月10日 下午9:54:13 */ public enum ManagerStatus { OK(1, "启用"), FREEZED(2, "冻结"), DELETED(3, "被删除"); int code; String message; ManagerStatus(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer value) { if (value == null) { return ""; } else { for (ManagerStatus ms : ManagerStatus.values()) { if (ms.getCode() == value) { return ms.getMessage(); } } return ""; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/MenuStatus.java ================================================ package cn.enilu.material.bean.constant.state; /** * 菜单的状态 * * @author fengshuonan * @Date 2017年1月22日 下午12:14:59 */ public enum MenuStatus { ENABLE(1, "启用"), DISABLE(0, "禁用"); int code; String message; MenuStatus(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer status) { if (status == null) { return ""; } else { for (MenuStatus s : MenuStatus.values()) { if (s.getCode() == status) { return s.getMessage(); } } return ""; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/constant/state/Order.java ================================================ package cn.enilu.material.bean.constant.state; /** * 数据库排序 * * @author fengshuonan * @Date 2017年5月31日20:48:41 */ public enum Order { ASC("asc"), DESC("desc"); private String des; Order(String des) { this.des = des; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/core/BussinessLog.java ================================================ package cn.enilu.material.bean.core; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; import cn.enilu.material.bean.dictmap.SystemDict; import java.lang.annotation.*; /** * 标记需要做业务日志的方法 * * @author fengshuonan * @date 2017-03-31 12:46 */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface BussinessLog { /** * 业务的名称,例如:"修改菜单" */ String value() default ""; /** * 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id" */ String key() default "id"; /** * 字典(用于查找key的中文名称和字段的中文名称) */ Class dict() default SystemDict.class; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/core/Permission.java ================================================ package cn.enilu.material.bean.core; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 权限注解 用于检查权限 规定访问权限 * * @example @Permission({roleID1,roleID2}) * @example @Permission */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Permission { String[] value() default {}; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/core/ShiroUser.java ================================================ package cn.enilu.material.bean.core; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.bean.vo.node.MenuNode; import java.io.Serializable; import java.util.List; /** * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息 * * @author fengshuonan * @date 2016年12月5日 上午10:26:43 */ public class ShiroUser implements Serializable { private static final long serialVersionUID = 1L; private Long id; // 主键ID private String account; // 账号 private String name; // 姓名 private Long deptId; // 部门id private List roleList; // 角色集 private String deptName; // 部门名称 private List roleNames; // 角色名称集 private List roleCodes;//角色编码 private User profile;//用户详细资料 private List titles;//用户可用菜单资源 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getDeptId() { return deptId; } public void setDeptId(Long deptId) { this.deptId = deptId; } public List getRoleList() { return roleList; } public void setRoleList(List roleList) { this.roleList = roleList; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } public List getRoleNames() { return roleNames; } public void setRoleNames(List roleNames) { this.roleNames = roleNames; } public List getRoleCodes() { return roleCodes; } public void setRoleCodes(List roleCodes) { this.roleCodes = roleCodes; } public User getProfile() { return profile; } public void setProfile(User profile) { this.profile = profile; } public List getTitles() { return titles; } public void setTitles(List titles) { this.titles = titles; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/CfgDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 字典map * * @author fengshuonan * @date 2017-05-06 15:43 */ public class CfgDict extends AbstractDictMap { @Override public void init() { put("cfgId","参数id"); put("cfgName","参数名称"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/CommonDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; public class CommonDict extends AbstractDictMap { @Override public void init() { put("id","ID"); put("name","名称"); put("code","编码"); put("title","标题"); put("remark","备注"); put("descript","备注"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/DeleteDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 用于删除业务的字典 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class DeleteDict extends AbstractDictMap { @Override public void init() { put("roleId","角色名称"); put("deptId", "部门名称"); put("menuId", "菜单名称"); put("dictId", "字典名称"); put("noticeId", "标题"); } @Override protected void initBeWrapped() { putFieldWrapperMethodName("roleId","getCacheObject"); putFieldWrapperMethodName("deptId","getCacheObject"); putFieldWrapperMethodName("menuId","getCacheObject"); putFieldWrapperMethodName("dictId","getCacheObject"); putFieldWrapperMethodName("noticeId","getCacheObject"); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/DeptDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 部门的映射 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class DeptDict extends AbstractDictMap { @Override public void init() { put("deptId", "部门名称"); put("id","部门名称"); put("num", "部门排序"); put("pid", "上级名称"); put("simplename", "部门简称"); put("fullname", "部门全称"); put("tips", "备注"); } @Override protected void initBeWrapped() { putFieldWrapperMethodName("deptId", "getDeptName"); putFieldWrapperMethodName("pid", "getDeptName"); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/DictMap.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 字典map * * @author fengshuonan * @date 2017-05-06 15:43 */ public class DictMap extends AbstractDictMap { @Override public void init() { put("dictId","字典名称"); put("dictName","字典名称"); put("dictValues","字典内容"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/LogDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 日志的字典 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class LogDict extends AbstractDictMap { @Override public void init() { put("tips","备注"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/MenuDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 菜单的字典 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class MenuDict extends AbstractDictMap { @Override public void init() { put("menuId","菜单id"); put("id","菜单id"); put("code","菜单编号"); put("pcode","菜单父编号"); put("name","菜单名称"); put("icon","菜单图标"); put("url","url地址"); put("num","菜单排序号"); put("levels","菜单层级"); put("tips","备注"); put("status","菜单状态"); put("isopen","是否打开"); put("",""); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/NoticeMap.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 通知的映射 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class NoticeMap extends AbstractDictMap { @Override public void init() { put("title", "标题"); put("content", "内容"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/RoleDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 角色的字典 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class RoleDict extends AbstractDictMap { @Override public void init() { put("roleId","角色名称"); put("num","角色排序"); put("pid","角色的父级"); put("name","角色名称"); put("deptid","部门名称"); put("tips","备注"); put("ids","资源名称"); } @Override protected void initBeWrapped() { putFieldWrapperMethodName("pid","getSingleRoleName"); putFieldWrapperMethodName("deptid","getDeptName"); putFieldWrapperMethodName("roleId","getSingleRoleName"); putFieldWrapperMethodName("ids","getMenuNames"); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/SystemDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 系统相关的字典 * * @author fengshuonan * @date 2017-05-06 15:48 */ public class SystemDict extends AbstractDictMap { @Override public void init() { } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/TaskDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 字典map * * @author fengshuonan * @date 2017-05-06 15:43 */ public class TaskDict extends AbstractDictMap { @Override public void init() { put("taskId","任务id"); put("name","任务名称"); } @Override protected void initBeWrapped() { } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/UserDict.java ================================================ package cn.enilu.material.bean.dictmap; import cn.enilu.material.bean.dictmap.base.AbstractDictMap; /** * 用户的字典 * * @author fengshuonan * @date 2017-05-06 15:01 */ public class UserDict extends AbstractDictMap { @Override public void init() { put("userId","账号"); put("avatar","头像"); put("account","账号"); put("name","名字"); put("birthday","生日"); put("sex","性别"); put("email","电子邮件"); put("phone","电话"); put("roleid","角色名称"); put("deptid","部门名称"); put("roleIds","角色名称集合"); } @Override protected void initBeWrapped() { putFieldWrapperMethodName("sex","getSexName"); putFieldWrapperMethodName("deptid","getDeptName"); putFieldWrapperMethodName("roleid","getSingleRoleName"); putFieldWrapperMethodName("userId","getUserAccountById"); putFieldWrapperMethodName("roleIds","getRoleName"); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dictmap/base/AbstractDictMap.java ================================================ package cn.enilu.material.bean.dictmap.base; import java.util.HashMap; /** * 字典映射抽象类 * * @author fengshuonan * @date 2017-05-06 14:58 */ public abstract class AbstractDictMap { protected HashMap dictory = new HashMap<>(); protected HashMap fieldWarpperDictory = new HashMap<>(); public AbstractDictMap(){ put("id","主键id"); init(); initBeWrapped(); } /** * 初始化字段英文名称和中文名称对应的字典 * * @author enilu.cn * @Date 2017/5/9 19:39 */ public abstract void init(); /** * 初始化需要被包装的字段(例如:性别为1:男,2:女,需要被包装为汉字) * * @author enilu.cn * @Date 2017/5/9 19:35 */ protected abstract void initBeWrapped(); public String get(String key) { return this.dictory.get(key); } public void put(String key, String value) { this.dictory.put(key, value); } public String getFieldWarpperMethodName(String key){ return this.fieldWarpperDictory.get(key); } public void putFieldWrapperMethodName(String key,String methodName){ this.fieldWarpperDictory.put(key,methodName); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/dto/UserDto.java ================================================ package cn.enilu.material.bean.dto; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; /** * 用户传输bean * * @author enilu.cn * @Date 2017/5/5 22:40 */ @Data public class UserDto{ private Long id; private String account; private String password; private String salt; private String name; @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday; private Integer sex; private String email; private String phone; private String roleid; private Long deptid; private Long eduorgid; private Integer status; private Date createtime; private Integer version; private String avatar; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/BaseEntity.java ================================================ package cn.enilu.material.bean.entity; import lombok.Data; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.io.Serializable; import java.util.Date; /** * Created on 2019/1/8 0002. * * @author enilu */ @MappedSuperclass @Data public abstract class BaseEntity implements Serializable { @Id @GeneratedValue private Long id; @CreatedDate @Column(name = "create_time",columnDefinition="DATETIME COMMENT '创建时间/注册时间'") private Date createTime; @Column(name = "create_by",columnDefinition="bigint COMMENT '创建人'") @CreatedBy private Long createBy; @LastModifiedDate @Column(name = "modify_time",columnDefinition="DATETIME COMMENT '最后更新时间'") private Date modifyTime; @LastModifiedBy @Column(name = "modify_by",columnDefinition="bigint COMMENT '最后更新人'") private Long modifyBy; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/message/Message.java ================================================ package cn.enilu.material.bean.entity.message; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; /** * 历史消息 */ @Data @Entity(name="t_message") @Table(appliesTo = "t_message",comment = "历史消息") public class Message extends BaseEntity { @Column(name="tpl_code",columnDefinition = "VARCHAR(32) COMMENT '模板编码'") private String tplCode; @Column(name="content",columnDefinition = "TEXT COMMENT '消息内容'") private String content; @Column(name="receiver",columnDefinition = "VARCHAR(64) COMMENT '接收者'") private String receiver; @Column(name="type",columnDefinition = "VARCHAR(32) COMMENT '消息类型,0:短信,1:邮件'") private Integer type; @Column(name="state",columnDefinition = "VARCHAR(32) COMMENT '消息类型,0:初始,1:成功,2:失败'") private Integer state; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/message/MessageSender.java ================================================ package cn.enilu.material.bean.entity.message; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; import javax.validation.constraints.NotBlank; /** * 消息发送者 */ @Data @Entity(name="t_message_sender") @Table(appliesTo = "t_message_sender",comment = "消息发送者") public class MessageSender extends BaseEntity { @Column(name="name",columnDefinition = "VARCHAR(64) COMMENT '名称'") @NotBlank(message = "名称并能为空") private String name; @Column(name="class_name",columnDefinition = "VARCHAR(64) COMMENT '发送类'") @NotBlank(message = "发送类不能为空") private String className; @Column(name="tpl_code",columnDefinition = "VARCHAR(64) COMMENT '短信运营商模板编号'") private String tplCode; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/message/MessageTemplate.java ================================================ package cn.enilu.material.bean.entity.message; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 消息模板 */ @Data @Entity(name="t_message_template") @Table(appliesTo = "t_message_template",comment = "消息模板") public class MessageTemplate extends BaseEntity { @Column(name="code",columnDefinition = "VARCHAR(32) COMMENT '编号'") @NotBlank(message = "编号不能为空") private String code; @NotBlank(message = "标题不能为空") @Column(name="title",columnDefinition = "VARCHAR(64) COMMENT '标题'") private String title; @NotBlank(message = "内容并能为空") @Column(name="content",columnDefinition = "TEXT COMMENT '内容'") private String content; @Column(name="cond",columnDefinition = "VARCHAR(32) COMMENT '发送条件'") private String cond; @Column(name="id_message_sender",columnDefinition = "BIGINT COMMENT '发送者id'") @NotNull(message = "发送器不能为空") private Long idMessageSender; @Column(name="type",columnDefinition = "VARCHAR(32) COMMENT '消息类型,0:短信,1:邮件'") private Integer type; @JoinColumn(name="id_message_sender", referencedColumnName = "id", columnDefinition = "BIGINT comment '发送者id'", insertable = false, updatable = false,foreignKey = @ForeignKey(name="none",value = ConstraintMode.NO_CONSTRAINT)) @ManyToOne(fetch = FetchType.EAGER) private MessageSender messageSender; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Cfg.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.validation.constraints.NotBlank; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_cfg") @Table(appliesTo = "t_sys_cfg",comment = "系统参数") @Data @EntityListeners(AuditingEntityListener.class) public class Cfg extends BaseEntity { @Column(name = "cfg_name",columnDefinition = "VARCHAR(256) COMMENT '参数名'") @NotBlank(message = "参数名不能为空") private String cfgName; @Column(name = "cfg_value",columnDefinition = "VARCHAR(512) COMMENT '参数值'") @NotBlank(message = "参数值不能为空") private String cfgValue; @Column(name = "cfg_desc",columnDefinition = "TEXT COMMENT '备注'") private String cfgDesc; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Dept.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_dept") @Table(appliesTo = "t_sys_dept",comment = "部门") @Data @EntityListeners(AuditingEntityListener.class) public class Dept extends BaseEntity { @Column private Integer num; @Column private Long pid; @Column private String pids; @Column private String simplename; @Column private String fullname; @Column private String tips; @Column private Integer version; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Dict.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_dict") @Table(appliesTo = "t_sys_dict",comment = "字典") @Data @EntityListeners(AuditingEntityListener.class) public class Dict extends BaseEntity { @Column(columnDefinition = "BIGINT COMMENT '字典组id'") private Long pid; @Column(columnDefinition = "VARCHAR(32) COMMENT '字典值'") private String value; @Column(columnDefinition = "VARCHAR(32) COMMENT '字典显示值'") private String name; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/FileInfo.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; @Data @Entity @Table(name="t_sys_file_info") public class FileInfo extends BaseEntity { @Column private String originalFileName; @Column private String realFileName; @Transient private String ablatePath; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/LoginLog.java ================================================ package cn.enilu.material.bean.entity.system; import lombok.Data; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import java.util.Date; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_login_log") @Table(appliesTo = "t_sys_login_log",comment = "登录日志") @Data public class LoginLog { @Id @GeneratedValue private Integer id; @Column private String logname; @Column private Integer userid; @Column private String succeed; @Column private String message; @Column private String ip; @CreationTimestamp @Column(name = "create_time",columnDefinition="DATETIME COMMENT '创建时间'") private Date createTime; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Menu.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name = "t_sys_menu") @Table(appliesTo = "t_sys_menu",comment = "菜单") @Data @EntityListeners(AuditingEntityListener.class) public class Menu extends BaseEntity { @Column private String code; @Column private String pcode; @Column private String pcodes; @Column private String name; @Column private String icon; @Column private String url; @Column private Integer num; @Column private Integer levels; @Column private Integer ismenu; @Column private String tips; @Column private Integer status; @Column private Integer isopen; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Notice.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_notice") @Table(appliesTo = "t_sys_notice",comment = "系统通知") @Data @EntityListeners(AuditingEntityListener.class) public class Notice extends BaseEntity { @Column private String title; @Column private Integer type; @Column private String content; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/OperationLog.java ================================================ package cn.enilu.material.bean.entity.system; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import java.util.Date; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name = "t_sys_operation_log") @Table(appliesTo= "t_sys_operation_log",comment = "操作日志") @Data public class OperationLog { @Id @GeneratedValue private Long id; @Column private String logtype; @Column private String logname; @Column private Integer userid; @Column private String classname; @Column private String method; @Column(name="create_time") private Date createTime; @Column private String succeed; @Column(columnDefinition = "TEXT COMMENT '详细信息'") private String message; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Relation.java ================================================ package cn.enilu.material.bean.entity.system; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_relation") @Table(appliesTo = "t_sys_relation",comment = "角色菜单关系") @Data public class Relation { @Id @GeneratedValue private Long id; @Column private Long menuid; @Column private Long roleid; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Role.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name="t_sys_role") @Table(appliesTo = "t_sys_role",comment = "角色") @Data @EntityListeners(AuditingEntityListener.class) public class Role extends BaseEntity { @Column private Integer num; @Column private Long pid; @Column private String name; @Column private Long deptid; @Column private String tips; @Column private Integer version; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/Task.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.validation.constraints.NotBlank; import java.util.Date; @Table(appliesTo="t_sys_task",comment = "定时任务") @Entity(name="t_sys_task") @Data @EntityListeners(AuditingEntityListener.class) public class Task extends BaseEntity { @Column(columnDefinition = "VARCHAR(50) COMMENT '任务名'") @NotBlank(message = "任务名不能为空") private String name; @Column(name="job_group", columnDefinition = "VARCHAR(50) COMMENT '任务组名'") private String jobGroup; @Column(name="job_class", columnDefinition = "VARCHAR(255) COMMENT '执行类'") @NotBlank(message = "执行类不能为空") private String jobClass; @Column(name="note", columnDefinition = "VARCHAR(255) COMMENT '任务说明'") private String note; @Column(name="cron", columnDefinition = "VARCHAR(50) COMMENT '定时规则'") @NotBlank(message = "定时规则不能为空") private String cron; @Column(name="concurrent", columnDefinition = "TINYINT COMMENT '是否允许并发'") private boolean concurrent; @Column(name="data", columnDefinition = "TEXT COMMENT '执行参数'") private String data; @Column(name="exec_at", columnDefinition = "DateTime COMMENT '执行时间'") private Date execAt; @Column(name="exec_result", columnDefinition = "text COMMENT '执行结果'") private String execResult; @Column(name="disabled", columnDefinition = "TINYINT COMMENT '是否禁用'") private boolean disabled; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/TaskLog.java ================================================ package cn.enilu.material.bean.entity.system; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import java.util.Date; /** *

* User: Yao * Date: 2017-06-22 11:12:48 */ @Table(appliesTo="t_sys_task_log",comment = "定时任务日志") @Entity(name="t_sys_task_log") @Data public class TaskLog{ public static final int EXE_FAILURE_RESULT = 0; public static final int EXE_SUCCESS_RESULT = 1; @Id @GeneratedValue private Long id; @Column private Long idTask; @Column(columnDefinition = "VARCHAR(50) COMMENT '任务名'") private String name; @Column(columnDefinition = "DATETime COMMENT '执行时间'") private Date execAt; @Column(columnDefinition = "INTEGER COMMENT '执行结果(成功:1、失败:0)'") private int execSuccess; @Column(columnDefinition = "VARCHAR(500) COMMENT '抛出异常'") private String jobException; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/system/User.java ================================================ package cn.enilu.material.bean.entity.system; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import java.util.Date; /** * Created on 2018/4/2 0002. * * @author enilu */ @Entity(name = "t_sys_user") @Table(appliesTo = "t_sys_user",comment = "系统管理员") @Data @EntityListeners(AuditingEntityListener.class) public class User extends BaseEntity { @Column private String avatar=""; @Column private String account; @Column private String password; @Column private String salt; @Column private String name; @Column private Date birthday; @Column private Integer sex; @Column private String email; @Column private String phone; @Column private String roleid; @Column private Long deptid; @Column private Integer status; @Column private Integer version; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/entity/test/Boy.java ================================================ package cn.enilu.material.bean.entity.test; import cn.enilu.material.bean.entity.BaseEntity; import lombok.Data; import org.hibernate.annotations.Table; import javax.persistence.Column; import javax.persistence.Entity; /** * 该实体用于测试生成代码 */ @Entity(name="t_test_boy") @Table(appliesTo = "t_test_boy",comment = "男孩") @Data public class Boy extends BaseEntity { @Column(columnDefinition = "VARCHAR(32) COMMENT '姓名'") private String name; @Column(columnDefinition = "INT COMMENT '年龄'") private Integer age; @Column(columnDefinition = "VARCHAR(12) COMMENT '生日'") private String birthday; @Column(name = "has_girl_friend",columnDefinition = "TINYINT COMMENT '是否有女朋友'") private Boolean hasGirFriend; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/BizExceptionEnum.java ================================================ package cn.enilu.material.bean.enumeration; import cn.enilu.material.bean.exception.ServiceExceptionEnum; /** * @Description 所有业务异常的枚举 * @author fengshuonan * @date 2016年11月12日 下午5:04:51 */ public enum BizExceptionEnum implements ServiceExceptionEnum { /** * 字典 */ DICT_EXISTED(400,"字典已经存在"), ERROR_CREATE_DICT(500,"创建字典失败"), ERROR_WRAPPER_FIELD(500,"包装字典属性失败"), /** * 文件上传 */ FILE_READING_ERROR(400,"FILE_READING_ERROR!"), FILE_NOT_FOUND(400,"FILE_NOT_FOUND!"), NOT_ALLOW(400, "不允许该操作"), UPLOAD_ERROR(500,"上传图片出错"), /** * 权限和数据问题 */ DB_RESOURCE_NULL(400,"数据库中没有该资源"), NO_PERMITION(405, "没有该操作权限"), REQUEST_INVALIDATE(400,"请求数据格式不正确"), INVALID_KAPTCHA(400,"验证码不正确"), CANT_DELETE_ADMIN(600,"不能删除超级管理员"), CANT_FREEZE_ADMIN(600,"不能冻结超级管理员"), CANT_CHANGE_ADMIN(600,"不能修改超级管理员"), /** * 账户问题 */ USER_ALREADY_REG(401,"该用户已经注册"), NO_THIS_USER(400,"没有此用户"), USER_NOT_EXISTED(400, "没有此用户"), ACCOUNT_FREEZED(401, "账号被冻结"), OLD_PWD_NOT_RIGHT(402, "原密码不正确"), TWO_PWD_NOT_MATCH(405, "两次输入密码不一致"), /** * 错误的请求 */ MENU_PCODE_COINCIDENCE(400,"菜单编号和副编号不能一致"), EXISTED_THE_MENU(400,"菜单编号重复,不能添加"), DICT_MUST_BE_NUMBER(400,"字典的值必须为数字"), REQUEST_NULL(400, "请求有错误"), SESSION_TIMEOUT(400, "会话超时"), SERVER_ERROR(500, "服务器异常"), CAN_NOT_DELETE(400,"数据被引用,无法删除"); BizExceptionEnum(int code, String message) { this.code = code; this.message = message; } private Integer code; private String message; @Override public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/ConfigKeyEnum.java ================================================ package cn.enilu.material.bean.enumeration; public enum ConfigKeyEnum { SYSTEM_FILE_UPLOAD_PATH("system.file.upload.path"), SYSTEM_APP_NAME("system.app.name"), SYSTEM_RESOURCE_VERSION("system.resource.version"), /** * 腾讯sms接口appid */ API_TENCENT_SMS_APPID("api.tencent.sms.appid"), /** * 腾讯sms接口appkey */ API_TENCENT_SMS_APPKEY("api.tencent.sms.appkey"), /** * 腾讯sms接口签名参数 */ API_TENCENT_SMS_SIGN("api.tencent.sms.sign"); private String value; ConfigKeyEnum(String value) { this.value = value; } public String getValue() { return value; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/ProjectEnum.java ================================================ package cn.enilu.material.bean.enumeration; /** * Created by deanyule on 17/8/16. */ public enum ProjectEnum { SCORE_CARD("ScoreCard"), DOLPHIN_PROD("dolphin"), SNOW_PRODUCT("PRJ_SNOW_PRODUCT"); private String value; ProjectEnum(String value) { this.value = value; } public String getValue() { return value; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/RedisQueueName.java ================================================ package cn.enilu.material.bean.enumeration; /** * redis队列名称 * Created by zt on 2017/8/25 0015. */ public enum RedisQueueName { CREDIT_LIMIT("credit_limit"),COLLECTION_TAG("collection_tag"); private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } RedisQueueName(String value) { this.value = value; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/SerialNumberEnum.java ================================================ package cn.enilu.material.bean.enumeration; /** * Created by deanyule on 17/4/13. * 系统各种编号枚举 */ public enum SerialNumberEnum { //交易编号 TR_DFYW_KLT("TRDFYWKLT", "代付-开联通"), TR_DFYW_CJ0("TRDFYWCJ0", "代付-畅捷"), TR_DFYW_LL0("TRDFYWLL0", "代付-连连"), TR_DFYW_RB0("TRDFYWRB0", "代付-融宝"), TR_DFYW_YSB("TRDFYWYSB", "代付-银生宝"), TR_DSYW_CJ0("TRDSYWCJ0", "代收-畅捷"), TR_DSYW_YSB("TRDSYWYSB", "代收-银生宝"), TR_DSYW_CPCN("TRDSYWCPC", "代收-中金"), TR_DSYW_BAOFU("TRDSYWBF", "代收-宝付"), TR_DSYW_FY0("TRDSYWFY0", "代收-富友"), TR_KJWG_CJ0("TRKJWGCJ0", "快捷-畅捷"), TR_KJWG_FY0("TRFY", "快捷-富友"), TR_KJWG_LL0("TRKJWGLL0", "快捷-连连"), TR_KJWG_WX0("TRKJWGWX0", "快捷-微信"), TR_KJTK_CJ0("TRKJTKCJ0", "快捷-退款-畅捷"), TR_KJZL_KLT("TRKJZLKLT", "快捷-直连-开联通"), /**第三方抽成*/ TR_CCSN_000("TRCCSN000", "第三方抽成"), TR_MANUL_000("TRMANUL00", "手工还款记账"), //借款申请编号 JK_SQC("JKSQC", "借款申请客户为Company"), JK_SQP("JKSQP", "借款申请客户为PC"), JK_SQS("JKSQS", "借款申请客户为ShareTransfer"), JK_SQM("JKSQM", "借款申请客户为Mobile"), JK_SQH("JKSQH", "借款申请客户为person on H5"), //协议模板编号 XDXY_L_C("XDXYLC","借款项目Lending Company"), XDXY_L_P("XDXYLP", "借款项目Lending Person"), XDXY_M_J("XDXYMJ", "居间协议M 借款居间J"), XDXY_R("XDXYR0", "注册协议R 0补位"), XDXY_Z("XDXYZ0", "征信协议Z 0补位"), //协议子编号 XDXY_L_P_0001("XDXYLP0001", "协议编号,借款项目Lending Person"), XDXY_R_P_0001("XDXYRP0001", "协议编号,代收协议签约"), //消息发送编号 XX_E("XXE", "发送方式Email"), XX_S("XXS", "发送方式SMS"), XX_L("XXL", "发送方式Letter"), XX_W("XXW", "发送方式微信"), //消息模板编号 XXMB_CL("XXMBCL","credit lending个人信贷"), XXMB_MC("XXMBMC", "小贷"), XXMB_YX("XXMBYX", "营销类"), //用户ID编号 US_JK_C("USJKC", "借款JK来源Company"), US_JK_P("USJKP", "借款JK 来源Person on PC"), US_JK_S("USJKS", "借款JK 来源Share transfer"), US_JK_M("USJKM", "借款JK 来源person on Mobile"), US_JK_H("USJKH", "借款JK 来源person on H5") ; private String code; private String desc; private SerialNumberEnum(String code, String desc) { this.code = code; this.desc = desc; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/TypeEnum.java ================================================ package cn.enilu.material.bean.enumeration; public class TypeEnum { /** * 输入数据的html类型 */ public enum DataItemShowType { /** * 0为文本框;1为下拉框;2为日期框 */ TEXT(0), SELECT(1), DATE(2); private int value; private DataItemShowType(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } /** * 调用类型枚举类 */ public enum InvokeTypeEnum { /** * 1:本地;1:远程 */ LOCAL("local"), REMOTE("remote"); private String value; private InvokeTypeEnum(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } /** * 调用结果枚举类 */ public enum InvokeStateEnum { /** * 1:查询成功;-1:查询无数据;2:查询异常 */ SUCCESS(1, "0000"), NO_DATA(-1, "0001"), ERROR(2, "9999"), ERROR_NO_WARN(2, "9999"); private int value; private String code; private InvokeStateEnum(int value, String code) { this.value = value; this.code = code; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public static boolean isError(InvokeStateEnum invokeState) { return InvokeStateEnum.ERROR == invokeState || InvokeStateEnum.ERROR_NO_WARN == invokeState; } public static boolean isNeedWarning(InvokeStateEnum invokeState) { return InvokeStateEnum.ERROR == invokeState; } public static InvokeStateEnum getNoData() { return InvokeStateEnum.NO_DATA; } public static InvokeStateEnum getSuccess() { return InvokeStateEnum.SUCCESS; } public static InvokeStateEnum getError() { return InvokeStateEnum.ERROR; } public static InvokeStateEnum getErrorNoWarn() { return InvokeStateEnum.ERROR_NO_WARN; } } /** * 订单来源枚举类 */ public enum OrderChannelEnum { WEB(0, "web"), WS(1, "接口"); private int value; private String name; private OrderChannelEnum(int value, String name) { this.value = value; this.name = name; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public enum AdminType { /** * 0为普通用户;1为超级管理员;2为机构管理员 */ NORMAL(0), SUPERADMIN(1), ORGADMIN(2); private int value; private AdminType(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public enum MatchType { /** * 0为模糊匹配;1为精确匹配; */ FUZZY(0), EXACT(1); private int value; private MatchType(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public enum MessageType { /** * 0为系统通知;1为订阅通知;2为用户发送 */ NOTIFY(0), SUBSCRIBE(1), SEND(2); private int value; private MessageType(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public enum Aspect { AFTERPAY("afterPay"), EVENTTRACK("eventTrack"), SNOWLOG("snowlog"), AFTERACTION("afterAction"), ENTERFINAL("enterFinal"), QUARTZJOB("quartzJob"), AUTOTRIGGER("autoTrigger"), PAYLIMIT("payLimit"); private String value; private Aspect(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public enum JobStatus { NONE("NONE", "未知"), NORMAL("NORMAL", "正常运行"), PAUSED("PAUSED", "暂停状态"), COMPLETE("COMPLETE", "运行完成"), ERROR("ERROR", "错误状态"), BLOCKED("BLOCKED", "锁定状态"); private JobStatus(String index, String name) { this.name = name; this.index = index; } private String index; private String name; public String getName() { return name; } public String getIndex() { return index; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/cms/BannerTypeEnum.java ================================================ package cn.enilu.material.bean.enumeration.cms; public enum BannerTypeEnum { INDEX("index"), NEWS("news"), CASE ("case"), PRODUCT("product"), SOLUTION("solution"); private String value; BannerTypeEnum(String value) { this.value = value; } public String getValue() { return value; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/enumeration/cms/ChannelEnum.java ================================================ package cn.enilu.material.bean.enumeration.cms; public enum ChannelEnum { NEWS(1L,"news"), PRODUCT(2L,"product"), SOLUTION(3L,"solution"), CASE(4L,"case"); private String value; private Long id; ChannelEnum(Long id,String value) { this.id = id; this.value = value; } public String getValue() { return value; } public Long getId(){ return id; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/ApplicationException.java ================================================ package cn.enilu.material.bean.exception; /** * 封装异常 * * @author fengshuonan * @Date 2017/12/28 下午10:32 */ public class ApplicationException extends RuntimeException { private Integer code; private String message; public ApplicationException(ServiceExceptionEnum serviceExceptionEnum) { this.code = serviceExceptionEnum.getCode(); this.message = serviceExceptionEnum.getMessage(); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/ExceptionEnum.java ================================================ package cn.enilu.material.bean.exception; /** * 异常枚举 * * @author fengshuonan * @Date 2017/12/28 下午10:33 */ public enum ExceptionEnum implements ServiceExceptionEnum{ /** * 其他 */ WRITE_ERROR(500,"渲染界面错误"), /** * 文件上传 */ FILE_READING_ERROR(400,"FILE_READING_ERROR!"), FILE_NOT_FOUND(400,"FILE_NOT_FOUND!"), /** * 错误的请求 */ REQUEST_NULL(400, "请求有错误"), SERVER_ERROR(500, "服务器异常"), TEST_NOT_ALLOWED(400, "演示环境不允许该操作"), ONLY_DEMO_ACCOUNT(400,"演示环境不能试用其他账号的登录"), TASK_CONFIG_ERROR(500, "定时任务配置错误"); ExceptionEnum(int code, String message) { this.code = code; this.message = message; } private Integer code; private String message; @Override public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/InvalidKaptchaException.java ================================================ package cn.enilu.material.bean.exception; /** * 验证码错误异常 * * @author fengshuonan * @date 2017-05-05 23:52 */ public class InvalidKaptchaException extends RuntimeException { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/MailException.java ================================================ package cn.enilu.material.bean.exception; public class MailException extends Exception { public MailException(String msg) { super(msg); } public MailException(String msg, Throwable e) { super(msg, e); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/ParamException.java ================================================ package cn.enilu.material.bean.exception; public class ParamException extends Exception { public ParamException(String msg) { super(msg); } public ParamException(String msg, Throwable e) { super(msg, e); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/ServiceExceptionEnum.java ================================================ package cn.enilu.material.bean.exception; /** * 抽象接口 * * @author fengshuonan * @date 2017-12-28-下午10:27 */ public interface ServiceExceptionEnum { /** * 获取异常编码 */ Integer getCode(); /** * 获取异常信息 */ String getMessage(); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/SlConnectException.java ================================================ package cn.enilu.material.bean.exception; import java.util.List; public class SlConnectException extends Exception { private static final long serialVersionUID = 1L; private String errorCode; private List details; public SlConnectException(String errorCode, String errorMessage) { super(errorMessage); this.errorCode = errorCode; } public String getErrorCode() { return errorCode; } public List getDetails() { return details; } public void setDetails(List details) { this.details = details; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/SlEvalException.java ================================================ package cn.enilu.material.bean.exception; import java.util.List; public class SlEvalException extends Exception { private static final long serialVersionUID = 1L; private String errorCode; private List details; public SlEvalException(String errorCode, String errorMessage) { super(errorMessage); this.errorCode = errorCode; } public String getErrorCode() { return errorCode; } public List getDetails() { return details; } public void setDetails(List details) { this.details = details; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/ValidException.java ================================================ package cn.enilu.material.bean.exception; public class ValidException extends RuntimeException { public ValidException(String msg) { super(msg); } public ValidException(String msg, Throwable e) { super(msg, e); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/XSException.java ================================================ package cn.enilu.material.bean.exception; /** * 定义通用异常 * code 存储异常代码 * @author czhou */ public class XSException extends Exception { private static final long serialVersionUID = 1L; private String code; public String getCode() { return this.code; } public XSException(String code, String message, Throwable t) { super(message, t); this.code = code; } public XSException(String code, String message) { this(code, message, null); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/exception/XSRuntimeException.java ================================================ package cn.enilu.material.bean.exception; @SuppressWarnings("serial") public class XSRuntimeException extends RuntimeException { public XSRuntimeException(String msg) { super(msg); } public XSRuntimeException(String msg, Throwable e) { super(msg, e); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/DictVo.java ================================================ package cn.enilu.material.bean.vo; /** * DictVo * * @author enilu * @version 2018/8/14 0014 */ public class DictVo { private String key; private String value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public DictVo() { } public DictVo(String key, String value) { this.key = key; this.value = value; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/QuartzJob.java ================================================ package cn.enilu.material.bean.vo; import lombok.Getter; import lombok.Setter; import java.io.Serializable; import java.util.Date; import java.util.Map; @Getter @Setter public class QuartzJob implements Serializable { private String jobId; private String jobName; private String jobGroup; private String jobClass; private String description; private String cronExpression; private boolean concurrent; private String jobStatus; private Date nextTime; private Date previousTime; private boolean disabled; private Map dataMap; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/SpringContextHolder.java ================================================ package cn.enilu.material.bean.vo; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * Spring的ApplicationContext的持有者,可以用静态方法的方式获取spring容器中的bean * * @author fengshuonan * @date 2016年11月27日 下午3:32:11 */ @Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextHolder.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { assertApplicationContext(); return applicationContext; } @SuppressWarnings("unchecked") public static T getBean(String beanName) { assertApplicationContext(); return (T) applicationContext.getBean(beanName); } public static T getBean(Class requiredType) { assertApplicationContext(); return applicationContext.getBean(requiredType); } private static void assertApplicationContext() { if (SpringContextHolder.applicationContext == null) { throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContextHolder!"); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/front/Ret.java ================================================ package cn.enilu.material.bean.vo.front; import lombok.Getter; import lombok.Setter; @Getter @Setter public class Ret { private Integer code; private String msg; private T data; private boolean success; public Ret() { } public Ret(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; this.success = "0000".equals(code); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("{"); builder.append("'code':").append(code).append(","); builder.append("'msg':").append(msg).append(","); builder.append("'success':").append(success).append(","); builder.append("}"); return builder.toString(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/front/Rets.java ================================================ package cn.enilu.material.bean.vo.front; public class Rets { public static final Integer SUCCESS = 20000; public static final Integer FAILURE = 9999; public static final Integer TOKEN_EXPIRE=50014; public static Ret success(Object data) { return new Ret(Rets.SUCCESS, "成功", data); } public static Ret failure(String msg) { return new Ret(Rets.FAILURE, msg, null); } public static Ret success() { return new Ret(Rets.SUCCESS, "成功", null); } public static Ret expire(){ return new Ret(Rets.TOKEN_EXPIRE,"token 过期",null); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/node/DeptNode.java ================================================ package cn.enilu.material.bean.vo.node; import cn.enilu.material.bean.entity.system.Dept; import java.util.ArrayList; import java.util.List; /** * DeptNode * * @author enilu * @version 2018/9/15 0015 */ public class DeptNode extends Dept { private List children = new ArrayList<>(10); public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/node/IsMenu.java ================================================ package cn.enilu.material.bean.vo.node; /** * 是否是菜单的枚举 * * @author fengshuonan * @date 2017年6月1日22:50:11 */ public enum IsMenu { YES(1, "是"), NO(0, "不是");//不是菜单的是按钮 int code; String message; IsMenu(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer status) { if (status == null) { return ""; } else { for (IsMenu s : IsMenu.values()) { if (s.getCode() == status) { return s.getMessage(); } } return ""; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/node/MenuNode.java ================================================ package cn.enilu.material.bean.vo.node; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * @author fengshuonan * @Description 菜单的节点 * @date 2016年12月6日 上午11:34:17 */ public class MenuNode implements Comparable , Serializable { /** * 节点id */ private Long id; /** * 父节点 */ private Long parentId; /** * 节点名称 */ private String name; /** * 按钮级别 */ private Integer levels; /** * 按钮级别 */ private Integer ismenu; private String isMenuName; private Integer status; private String statusName; /** * 按钮的排序 */ private Integer num; /** * 节点的url */ private String url; /** * 节点图标 */ private String icon; /** * 菜单编码 */ private String code; /** * 子节点的集合 */ private List children = new ArrayList<>(10); /** * 查询子节点时候的临时集合 */ private List linkedList = new ArrayList(); public MenuNode() { super(); } public MenuNode(Long id, Long parentId) { super(); this.id = id; this.parentId = parentId; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Integer getLevels() { return levels; } public void setLevels(Integer levels) { this.levels = levels; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public static MenuNode createRoot() { return new MenuNode(0L, -1L); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } public Integer getIsmenu() { return ismenu; } public void setIsmenu(Integer ismenu) { this.ismenu = ismenu; } public String getIsMenuName() { return ismenu == 1 ?"是":"否"; } public void setIsMenuName(String isMenuName) { this.isMenuName = isMenuName; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getStatusName() { return status == 1 ?"启用":"禁用"; } public void setStatusName(String statusName) { this.statusName = statusName; } @Override public String toString() { return "MenuNode{" + "id=" + id + ", parentId=" + parentId + ", name='" + name + '\'' + ", levels=" + levels + ", num=" + num + ", url='" + url + '\'' + ", icon='" + icon + '\'' + ", children=" + children + ", linkedList=" + linkedList + '}'; } @Override public int compareTo(Object o) { MenuNode menuNode = (MenuNode) o; Integer num = menuNode.getNum(); if (num == null) { num = 0; } return this.num.compareTo(num); } /** * 构建整个菜单树 * * @author fengshuonan */ public void buildNodeTree(List nodeList) { for (MenuNode treeNode : nodeList) { List linkedList = treeNode.findChildNodes(nodeList, treeNode.getId()); if (linkedList.size() > 0) { treeNode.setChildren(linkedList); } } } /** * 查询子节点的集合 * * @author fengshuonan */ public List findChildNodes(List nodeList, Long parentId) { if (nodeList == null && parentId == null) return null; for (Iterator iterator = nodeList.iterator(); iterator.hasNext(); ) { MenuNode node = (MenuNode) iterator.next(); // 根据传入的某个父节点ID,遍历该父节点的所有子节点 if (node.getParentId() != 0 && parentId.equals(node.getParentId())) { recursionFn(nodeList, node, parentId); } } return linkedList; } /** * 遍历一个节点的子节点 * * @author fengshuonan */ public void recursionFn(List nodeList, MenuNode node, Long pId) { List childList = getChildList(nodeList, node);// 得到子节点列表 if (childList.size() > 0) {// 判断是否有子节点 if (node.getParentId().equals(pId)) { linkedList.add(node); } Iterator it = childList.iterator(); while (it.hasNext()) { MenuNode n = (MenuNode) it.next(); recursionFn(nodeList, n, pId); } } else { if (node.getParentId().equals(pId)) { linkedList.add(node); } } } /** * 得到子节点列表 * * @author fengshuonan */ private List getChildList(List list, MenuNode node) { List nodeList = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { MenuNode n = (MenuNode) it.next(); if (n.getParentId().equals(node.getId())) { nodeList.add(n); } } return nodeList; } /** * 清除掉按钮级别的资源 * * @date 2017年2月19日 下午11:04:11 */ public static List clearBtn(List nodes) { ArrayList noBtns = new ArrayList(); for (MenuNode node : nodes) { if(node.getIsmenu() == IsMenu.YES.getCode()){ noBtns.add(node); } } return noBtns; } /** * 清除所有二级菜单 * * @date 2017年2月19日 下午11:18:19 */ public static List clearLevelTwo(List nodes) { ArrayList results = new ArrayList(); for (MenuNode node : nodes) { Integer levels = node.getLevels(); if (levels.equals(1)) { results.add(node); } } return results; } /** * 构建菜单列表 * * @date 2017年2月19日 下午11:18:19 */ public static List buildTitle(List nodes) { List clearBtn = clearBtn(nodes); new MenuNode().buildNodeTree(clearBtn); List menuNodes = clearLevelTwo(clearBtn); //对菜单排序 Collections.sort(menuNodes); //对菜单的子菜单进行排序 for (MenuNode menuNode : menuNodes) { if (menuNode.getChildren() != null && menuNode.getChildren().size() > 0) { Collections.sort(menuNode.getChildren()); } } return menuNodes; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/node/Node.java ================================================ package cn.enilu.material.bean.vo.node; import lombok.Data; import java.util.ArrayList; import java.util.List; /** * Node * * @author enilu * @version 2018/11/24 0024 */ @Data public class Node { private Long id; private Long pid; private String name; private Boolean checked; private List children = new ArrayList<>(10); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/node/ZTreeNode.java ================================================ package cn.enilu.material.bean.vo.node; /** * * jquery ztree 插件的节点 * * @author fengshuonan * @date 2017年2月17日 下午8:25:14 */ public class ZTreeNode { private Long id; //节点id private Long pId;//父节点id private String code;//编号 private String name;//节点名称 private Boolean open;//是否打开节点 private Boolean checked;//是否被选中 private Object nodeData;//自定义数据 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getpId() { return pId; } public void setpId(Long pId) { this.pId = pId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Boolean getOpen() { return open; } public void setOpen(Boolean open) { this.open = open; } public Boolean getIsOpen() { return open; } public void setIsOpen(Boolean open) { this.open = open; } public Boolean getChecked() { return checked; } public void setChecked(Boolean checked) { this.checked = checked; } public Object getNodeData() { return nodeData; } public void setNodeData(Object nodeData) { this.nodeData = nodeData; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public static ZTreeNode createParent(){ ZTreeNode zTreeNode = new ZTreeNode(); zTreeNode.setChecked(true); zTreeNode.setId(0L); zTreeNode.setName("顶级"); zTreeNode.setOpen(true); zTreeNode.setpId(0L); zTreeNode.setCode(""); return zTreeNode; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/query/DynamicSpecifications.java ================================================ package cn.enilu.material.bean.vo.query; import cn.enilu.material.utils.Lists; import org.apache.commons.lang3.StringUtils; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.*; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * descript * * @author :enilu * @date :Created in 2019/6/30 16:04 */ public class DynamicSpecifications { public static Specification bySearchFilter(final Collection filters, final Class entityClazz) { return new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { if (filters!=null && !filters.isEmpty()) { List predicates = Lists.newArrayList(); for (SearchFilter filter : filters) { // nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性 String[] names = StringUtils.split(filter.fieldName, "."); Path expression = root.get(names[0]); for (int i = 1; i < names.length; i++) { expression = expression.get(names[i]); } // logic operator switch (filter.operator) { case EQ: predicates.add(builder.equal(expression, filter.value)); break; case LIKE: predicates.add(builder.like(expression, "%" + filter.value + "%")); break; case GT: predicates.add(builder.greaterThan(expression, (Comparable) filter.value)); break; case LT: predicates.add(builder.lessThan(expression, (Comparable) filter.value)); break; case GTE: predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value)); break; case LTE: predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value)); break; case IN: if(filter.value.getClass().isArray()){ predicates.add(expression.in((Object[]) filter.value)); }else { predicates.add(expression.in((ArrayList) filter.value)); } break; case ISNULL: predicates.add(expression.isNull()); break; case ISNOTNULL: predicates.add(expression.isNotNull()); break; } } // 将所有条件用 and 联合起来 if (!predicates.isEmpty()) { return builder.and(predicates.toArray(new Predicate[predicates.size()])); } } return builder.conjunction(); } }; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/query/MutiStrFactory.java ================================================ package cn.enilu.material.bean.vo.query; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.utils.StrKit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 组合字符串生产者 * * @author fengshuonan * @date 2017-04-27 16:42 */ public class MutiStrFactory { /** * 每个条目之间的分隔符 */ public static final String ITEM_SPLIT = ";"; /** * 属性之间的分隔符 */ public static final String ATTR_SPLIT = ":"; /** * 拼接字符串的id */ public static final String MUTI_STR_ID = "ID"; /** * 拼接字符串的key */ public static final String MUTI_STR_KEY = "KEY"; /** * 拼接字符串的value */ public static final String MUTI_STR_VALUE = "VALUE"; /** * 解析一个组合字符串(例如: "1:启用;2:禁用;3:冻结" 这样的字符串) * * @author fengshuonan * @Date 2017/4/27 16:44 */ public static List> parseKeyValue(String mutiString){ if(ToolUtil.isEmpty(mutiString)){ return new ArrayList<>(); }else{ ArrayList> results = new ArrayList<>(); String[] items = StrKit.split(StrKit.removeSuffix(mutiString, ITEM_SPLIT), ITEM_SPLIT); for (String item : items) { String[] attrs = item.split(ATTR_SPLIT); HashMap itemMap = new HashMap<>(); itemMap.put(MUTI_STR_KEY,attrs[0]); itemMap.put(MUTI_STR_VALUE,attrs[1]); results.add(itemMap); } return results; } } /** * 解析id:key:value这样类型的字符串 * * @author fengshuonan * @Date 2017/4/28 11:06 */ public static List> parseIdKeyValue(String mutiString){ if(ToolUtil.isEmpty(mutiString)){ return new ArrayList<>(); }else{ ArrayList> results = new ArrayList<>(); String[] items = StrKit.split(StrKit.removeSuffix(mutiString, ITEM_SPLIT), ITEM_SPLIT); for (String item : items) { String[] attrs = item.split(ATTR_SPLIT); HashMap itemMap = new HashMap<>(); itemMap.put(MUTI_STR_ID,attrs[0]); itemMap.put(MUTI_STR_KEY,attrs[1]); itemMap.put(MUTI_STR_VALUE,attrs[2]); results.add(itemMap); } return results; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/query/Page.java ================================================ package cn.enilu.material.bean.vo.query; import cn.enilu.material.utils.Lists; import cn.enilu.material.utils.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * Created on 2018/3/22 0022. * * @author enilu */ public class Page { /** * 该操作只是为了忽略RowBounds属性 * * */ private transient int offset; /** * 该操作只是为了忽略RowBounds属性 * * */ private transient int limit; /** * 总数 */ private int total; /** * 每页显示条数,默认 10 */ private int size = 10; /** * 总页数 */ private int pages; /** * 当前页 */ private int current = 1; /** * 查询总记录数(默认 true) */ private transient boolean searchCount = true; /** * 开启排序(默认 true) 只在代码逻辑判断 并不截取sql分析 * * **/ private transient boolean openSort = true; /** *

* SQL 排序 ASC 集合 *

*/ private transient List ascs; /** *

* SQL 排序 DESC 集合 *

*/ private transient List descs; /** * 是否为升序 ASC( 默认: true ) * * @see #ascs * @see #descs */ private transient boolean isAsc = true; /** *

* SQL 排序 ORDER BY 字段,例如: id DESC(根据id倒序查询) *

*

* DESC 表示按倒序排序(即:从大到小排序)
* ASC 表示按正序排序(即:从小到大排序) * * @see #ascs * @see #descs *

*/ private transient String orderByField; public Page() { } public Page(int current, int size, String orderByField) { this.setOrderByField(orderByField); } public Page(int current, int size, String orderByField, boolean isAsc) { this(current, size, orderByField); this.setAsc(isAsc); } /** *

* 分页构造函数 *

* * @param current 当前页 * @param size 每页显示条数 */ public Page(int current, int size) { this(current,size,true); } public Page(int current, int size, boolean searchCount) { this(current, size, searchCount, true); } public Page(int current, int size, boolean searchCount, boolean openSort) { setOffset(offsetCurrent(current, size)); setLimit(size); if (current > 1) { this.current = current; } this.size = size; this.searchCount = searchCount; this.openSort = openSort; } protected static int offsetCurrent(int current, int size) { if (current > 0) { return (current - 1) * size; } return 0; } public int offsetCurrent() { return offsetCurrent(this.current, this.size); } public boolean hasPrevious() { return this.current > 1; } public boolean hasNext() { return this.current < this.pages; } public int getTotal() { return total; } public Page setTotal(int total) { this.total = total; return this; } public int getSize() { return size; } public Page setSize(int size) { this.size = size; return this; } public int getPages() { if (this.size == 0) { return 0; } this.pages = this.total / this.size; if (this.total % this.size != 0) { this.pages++; } return this.pages; } public int getCurrent() { return current; } public Page setCurrent(int current) { this.current = current; return this; } public boolean isSearchCount() { return searchCount; } public Page setSearchCount(boolean searchCount) { this.searchCount = searchCount; return this; } /** * @see #ascs * @see #descs */ @Deprecated public String getOrderByField() { return orderByField; } /** * @see #ascs * @see #descs */ public Page setOrderByField(String orderByField) { if (StringUtils.isNotEmpty(orderByField)) { this.orderByField = orderByField; } return this; } public boolean isOpenSort() { return openSort; } public Page setOpenSort(boolean openSort) { this.openSort = openSort; return this; } public List getAscs() { return orders(isAsc, ascs); } private List orders(boolean condition, List columns) { if (condition && StringUtils.isNotEmpty(orderByField)) { if (columns == null) { columns = new ArrayList<>(); } if (!columns.contains(orderByField)) { columns.add(orderByField); } } return columns; } public Page setAscs(List ascs) { this.ascs = ascs; return this; } public List getDescs() { return orders(!isAsc, descs); } public Page setDescs(List descs) { this.descs = descs; return this; } /** * @see #ascs * @see #descs */ @Deprecated public boolean isAsc() { return isAsc; } /** * @see #ascs * @see #descs */ public Page setAsc(boolean isAsc) { this.isAsc = isAsc; return this; } public int getOffset() { return offset; } public Page setOffset(int offset) { this.offset = offset; return this; } public int getLimit() { return limit; } public Page setLimit(int limit) { this.limit = limit; return this; } /** * 查询数据列表 */ private List records = Collections.emptyList(); /** * 查询参数 */ private transient Map condition; private transient List filters; public List getRecords() { return records; } public Page setRecords(List records) { this.records = records; return this; } public Map getCondition() { return condition; } public Page setCondition(Map condition) { this.condition = condition; return this; } public List getFilters() { return filters; } public void setFilters(List filters) { this.filters = filters; } public void addFilter(SearchFilter filter){ if(filter==null){ return ; } if(filters==null){ filters = Lists.newArrayList(); } filters.add(filter); } public void addFilter(String fieldName, SearchFilter.Operator operator, Object value){ if(!StringUtils.isNullOrEmpty(value)){ addFilter(SearchFilter.build(fieldName,operator,value)); } } public void addFilter(String fieldName, SearchFilter.Operator operator){ addFilter(SearchFilter.build(fieldName,operator)); } @Override public String toString() { StringBuilder pg = new StringBuilder(); pg.append(" Page:{ [").append(super.toString()).append("], "); if (records != null) { pg.append("records-size:").append(records.size()); } else { pg.append("records is null"); } return pg.append(" }").toString(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/bean/vo/query/SearchFilter.java ================================================ package cn.enilu.material.bean.vo.query; import cn.enilu.material.utils.Maps; import org.apache.commons.lang3.StringUtils; import java.util.Map; /** * descript * * @author :enilu * @date :Created in 2019/6/30 16:02 */ public class SearchFilter { public enum Operator { EQ, LIKE, GT, LT, GTE, LTE,IN,ISNULL,ISNOTNULL } public String fieldName; public Object value; public Operator operator = Operator.EQ; public static SearchFilter build(String fieldName, Operator operator, Object value){ return new SearchFilter(fieldName,operator,value); } public static SearchFilter build(String fieldName, Object val){ return new SearchFilter(fieldName,Operator.EQ,val); } public SearchFilter(String fieldName, Object val) { this.fieldName = fieldName; this.value = val; } public SearchFilter(String fieldName, Operator operator, Object value) { this.fieldName = fieldName; this.value = value; this.operator = operator; } /** * searchParams中key的格式为OPERATOR_FIELDNAME */ public static Map parse(Map searchParams) { Map filters = Maps.newHashMap(); for (Map.Entry entry : searchParams.entrySet()) { // 过滤掉空值 String key = entry.getKey(); Object value = entry.getValue(); /*if (StringUtils.isBlank((String) value)) { continue; }*/ // 拆分operator与filedAttribute String[] names = StringUtils.split(key, "_"); if (names.length != 2) { throw new IllegalArgumentException(key + " is not a valid search filter name"); } String filedName = names[1]; Operator operator = Operator.valueOf(names[0]); // 创建searchFilter SearchFilter filter = new SearchFilter(filedName, operator, value); filters.put(key, filter); } return filters; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/BaseRepository.java ================================================ package cn.enilu.material.dao; import cn.enilu.material.bean.vo.query.SearchFilter; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import java.io.Serializable; import java.util.List; import java.util.Map; /** * 封装基础的dao接口 * * @author :enilu * @date :Created in 2019/6/29 12:50 */ @NoRepositoryBean public interface BaseRepository extends JpaRepository , PagingAndSortingRepository , JpaSpecificationExecutor { List queryBySql(String sql); List queryBySql(String sql, List filter); List query(String sql); Object getBySql(String sql); T get(String sql); int execute(String sql); Class getDataClass(); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/BaseRepositoryFactoryBean.java ================================================ package cn.enilu.material.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.util.Assert; import javax.persistence.EntityManager; import java.io.Serializable; /** * 自定义repository工厂类 * * @author :enilu * @date :Created in 2019/6/29 12:59 */ public class BaseRepositoryFactoryBean, T, ID extends Serializable> extends JpaRepositoryFactoryBean { public BaseRepositoryFactoryBean(Class repositoryInterface) { super(repositoryInterface); } @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new BaseRepositoryFactory(entityManager); } private static class BaseRepositoryFactory extends JpaRepositoryFactory { private final EntityManager entityManager; public BaseRepositoryFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } @Override protected JpaRepositoryImplementation getTargetRepository(RepositoryInformation information, EntityManager entityManager) { JpaEntityInformation entityInformation = this.getEntityInformation(information.getDomainType()); Object repository = this.getTargetRepositoryViaReflection(information, new Object[]{entityInformation, entityManager}); Assert.isInstanceOf(BaseRepositoryImpl.class, repository); return (JpaRepositoryImplementation)repository; } @Override protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { return BaseRepositoryImpl.class; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/BaseRepositoryImpl.java ================================================ package cn.enilu.material.dao; import cn.enilu.material.bean.vo.query.SearchFilter; import org.hibernate.SQLQuery; import org.hibernate.query.internal.NativeQueryImpl; import org.hibernate.transform.Transformers; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import javax.persistence.EntityManager; import javax.persistence.Query; import java.io.Serializable; import java.util.List; import java.util.Map; /** * 基础dao实现类 * * @author :enilu * @date :Created in 2019/6/29 12:53 */ public class BaseRepositoryImpl extends SimpleJpaRepository implements BaseRepository { private final EntityManager entityManager; private Class klass; BaseRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; this.klass = (Class) entityInformation.getJavaType(); } @Override public List queryBySql(String sql) { return queryBySql(sql,null); } @Override public List queryBySql(String sql, List filters) { Query query = entityManager.createNativeQuery(sql); query.unwrap(NativeQueryImpl.class) .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); if(filters!=null&&!filters.isEmpty()) { for (SearchFilter filter : filters) { query.setParameter(filter.fieldName, filter.value); } } List list = query.getResultList(); return list; } @Override public Object getBySql(String sql) { List list = entityManager.createNativeQuery(sql).getResultList(); if(list.isEmpty()){ return null; } return list.get(0); } @Override public T get(String sql) { List list = entityManager.createNativeQuery(sql,klass).getResultList(); return list.get(0); } @Override public int execute(String sql) { return entityManager.createNativeQuery(sql).executeUpdate(); } @Override public Class getDataClass() { return klass; } @Override public List query(String sql) { return entityManager.createNativeQuery(sql,klass).getResultList(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/DaoConfiguration.java ================================================ package cn.enilu.material.dao; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; /** * Name: DabConfiguration
* User: Yao
* Date: 2018/2/27
* Time: 13:54
*/ @Configuration @EnableJpaRepositories("cn.enilu.material.dao") public class DaoConfiguration { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/BaseCache.java ================================================ package cn.enilu.material.dao.cache; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.service.system.impl.ConstantFactory; /** * @author :enilu * @date :Created in 2020/4/26 19:07 */ public abstract class BaseCache implements Cache { @Override public void cache() { SpringContextHolder.getBean(ConstantFactory.class).cleanLocalCache(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/Cache.java ================================================ package cn.enilu.material.dao.cache; /** * 顶级缓存接口 */ public interface Cache { /** * 将数据库中的数据加载到缓存中 */ void cache(); /** * 获取缓存数据 * * @param key * @return */ Object get(String key); /** * 设置缓存数据 * * @param key * @param val */ void set(String key, Object val); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/CacheDao.java ================================================ package cn.enilu.material.dao.cache; import java.io.Serializable; /** * CacheDao * * @author enilu * @version 2018/9/12 0012 */ public interface CacheDao { /** * 设置hash key值 * * @param key * @param k * @param val */ void hset(Serializable key, Serializable k, Object val); /** * 获取hash key值 * * @param key * @param k * @return */ Serializable hget(Serializable key, Serializable k); /** * 获取hash key值 * @param key * @param k * @param klass * @param * @return */ T hget(Serializable key, Serializable k,Class klass); /** * 设置key值,超时失效 * * @param key * @param val */ void set(Serializable key, Object val); /** * 获取key值 * * @param key * @return */ T get(Serializable key,Class klass); String get(Serializable key); void del(Serializable key); void hdel(Serializable key, Serializable k); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/ConfigCache.java ================================================ package cn.enilu.material.dao.cache; /** * 全局配置数据访问 */ public interface ConfigCache extends Cache { /** * 获取全局配置参数值,可选本地缓存 * @param key * @return */ String get(String key, boolean local); /** * 获取全局配置参数值(带默认值) * * @param key the key * @param def the default value * @return the config */ String get(String key, String def); /** * 删除缓存 * @param key * @param val */ void del(String key, String val); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/DictCache.java ================================================ package cn.enilu.material.dao.cache; import cn.enilu.material.bean.entity.system.Dict; import java.util.List; /** * 字典缓存 * * @author zt * @version 2018/12/23 0023 */ public interface DictCache extends Cache{ List getDictsByPname(String dictName); String getDict(Long dictId); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/TokenCache.java ================================================ package cn.enilu.material.dao.cache; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.dao.cache.impl.EhcacheDao; import cn.enilu.material.utils.HttpKit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 用户登录时,生成的Token与用户ID的对应关系 */ @Service public class TokenCache { @Autowired private EhcacheDao ehcacheDao; public void put(String token, Long idUser) { ehcacheDao.hset(EhcacheDao.SESSION,token, idUser); } public Long get(String token) { return ehcacheDao.hget(EhcacheDao.SESSION,token,Long.class); } public Long getIdUser(){ return ehcacheDao.hget(EhcacheDao.SESSION,HttpKit.getToken(),Long.class ); } public void remove(String token) { ehcacheDao.hdel(EhcacheDao.SESSION,token); } public void setUser(String token, ShiroUser shiroUser){ ehcacheDao.hset(EhcacheDao.SESSION,token+"user",shiroUser); } public ShiroUser getUser(String token){ return ehcacheDao.hget(EhcacheDao.SESSION,token+"user",ShiroUser.class); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/impl/ConfigCacheImpl.java ================================================ package cn.enilu.material.dao.cache.impl; import cn.enilu.material.bean.entity.system.Cfg; import cn.enilu.material.dao.cache.BaseCache; import cn.enilu.material.dao.cache.CacheDao; import cn.enilu.material.dao.cache.ConfigCache; import cn.enilu.material.dao.system.CfgRepository; import cn.enilu.material.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * 全局参数缓存实现类 * * @author enilu * @version 2018/12/20 0020 */ @Service public class ConfigCacheImpl extends BaseCache implements ConfigCache { private static final Logger logger = LoggerFactory.getLogger(ConfigCacheImpl.class); @Autowired private CfgRepository cfgRepository; @Autowired private CacheDao cacheDao; @Override public Object get(String key) { return (String) cacheDao.hget(EhcacheDao.CONSTANT,key); } @Override public String get(String key, boolean local) { String ret = null; if(local) { ret = (String) get(key); }else{ ret = cfgRepository.findByCfgName(key).getCfgValue(); set(key,ret); } return ret; } @Override public String get(String key, String def) { String ret = (String) get(key); if(StringUtils.isEmpty(ret)){ return ret; } return ret; } @Override public void set(String key, Object val) { cacheDao.hset(EhcacheDao.CONSTANT,key,val); } @Override public void del(String key, String val) { cacheDao.hdel(EhcacheDao.CONSTANT,val); } @Override public void cache() { super.cache(); logger.info("reset config cache"); List list = cfgRepository.findAll(); if (list != null && !list.isEmpty()) { for (Cfg cfg : list) { if (StringUtils.isNotEmpty(cfg.getCfgName()) && StringUtils.isNotEmpty(cfg.getCfgValue())) { set(cfg.getCfgName(),cfg.getCfgValue()); } } } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/impl/DictCacheImpl.java ================================================ package cn.enilu.material.dao.cache.impl; import cn.enilu.material.bean.constant.cache.CacheKey; import cn.enilu.material.bean.entity.system.Dict; import cn.enilu.material.dao.cache.BaseCache; import cn.enilu.material.dao.cache.CacheDao; import cn.enilu.material.dao.cache.DictCache; import cn.enilu.material.dao.system.DictRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * DictCacheImpl * * @author zt * @version 2018/12/23 0023 */ @Component public class DictCacheImpl extends BaseCache implements DictCache { @Autowired private DictRepository dictRepository; @Autowired private CacheDao cacheDao; @Override public List getDictsByPname(String dictName) { return (List) cacheDao.hget(EhcacheDao.CONSTANT, CacheKey.DICT + dictName, List.class); } @Override public String getDict(Long dictId) { return (String) get(CacheKey.DICT_NAME + String.valueOf(dictId)); } @Override public void cache() { super.cache(); List list = dictRepository.findByPid(0L); for (Dict dict : list) { List children = dictRepository.findByPid(dict.getId()); if (children.isEmpty()) { continue; } set(String.valueOf(dict.getId()), children); set(dict.getName(), children); for (Dict child : children) { set(CacheKey.DICT_NAME + String.valueOf(child.getId()), child.getName()); } } } @Override public Object get(String key) { return cacheDao.hget(EhcacheDao.CONSTANT, CacheKey.DICT + key); } @Override public void set(String key, Object val) { cacheDao.hset(EhcacheDao.CONSTANT, CacheKey.DICT + key, val); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/impl/EhcacheDao.java ================================================ package cn.enilu.material.dao.cache.impl; import cn.enilu.material.dao.cache.CacheDao; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.io.Serializable; /** * EhcacheDao * * @author enilu * @version 2018/9/12 0012 */ @Component public class EhcacheDao implements CacheDao { //缓存常量,永不过期 public static final String CONSTANT = "CONSTANT"; public static final String SESSION = "SESSION"; @Resource private CacheManager cacheManager; @Override public void hset(Serializable key, Serializable k, Object val) { Cache cache = cacheManager.getCache(String.valueOf(key)); cache.put(k,val); } @Override public Serializable hget(Serializable key, Serializable k) { Cache cache = cacheManager.getCache(String.valueOf(key)); return cache.get(k,String.class); } @Override public T hget(Serializable key, Serializable k,Class klass) { Cache cache = cacheManager.getCache(String.valueOf(key)); return cache.get(k,klass); } @Override public void set(Serializable key, Object val) { Cache cache = cacheManager.getCache(CONSTANT); cache.put(key,val); } @Override public T get(Serializable key,Class klass) { return cacheManager.getCache(CONSTANT).get(String.valueOf(key),klass); } @Override public String get(Serializable key) { return cacheManager.getCache(CONSTANT).get(String.valueOf(key),String.class); } @Override public void del(Serializable key) { cacheManager.getCache(CONSTANT).put(String.valueOf(key),null); } @Override public void hdel(Serializable key, Serializable k) { cacheManager.getCache(String.valueOf(key)).put(String.valueOf(k),null); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/cache/package-info.java ================================================ /** * package-info * * @version 2018/9/11 0011 * @author enilu */ package cn.enilu.material.dao.cache; ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/message/MessageRepository.java ================================================ package cn.enilu.material.dao.message; import cn.enilu.material.bean.entity.message.Message; import cn.enilu.material.dao.BaseRepository; import java.util.ArrayList; public interface MessageRepository extends BaseRepository { void deleteAllByIdIn(ArrayList list); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/message/MessagesenderRepository.java ================================================ package cn.enilu.material.dao.message; import cn.enilu.material.bean.entity.message.MessageSender; import cn.enilu.material.dao.BaseRepository; public interface MessagesenderRepository extends BaseRepository { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/message/MessagetemplateRepository.java ================================================ package cn.enilu.material.dao.message; import cn.enilu.material.bean.entity.message.MessageTemplate; import cn.enilu.material.dao.BaseRepository; import java.util.List; public interface MessagetemplateRepository extends BaseRepository { MessageTemplate findByCode(String code); List findByIdMessageSender(Long idMessageSender); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/CfgRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Cfg; import cn.enilu.material.dao.BaseRepository; public interface CfgRepository extends BaseRepository { Cfg findByCfgName(String cfgName); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/DeptRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Dept; import cn.enilu.material.dao.BaseRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface DeptRepository extends BaseRepository { List findByPidsLike(String pid); @Query(nativeQuery = true,value = "SELECT id, pid AS pId, simplename AS NAME, ( CASE WHEN (pId = 0 OR pId IS NULL) THEN 'true' ELSE 'false' END ) AS isOpen FROM t_sys_dept") List tree(); List findBySimplenameLikeOrFullnameLike(String name,String name2); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/DictRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Dict; import cn.enilu.material.dao.BaseRepository; import java.util.List; public interface DictRepository extends BaseRepository { List findByPid(Long pid); List findByNameAndPid(String name,Long pid); List findByNameLike(String name); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/FileInfoRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.FileInfo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.PagingAndSortingRepository; public interface FileInfoRepository extends PagingAndSortingRepository , JpaRepository, JpaSpecificationExecutor { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/LoginLogRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.LoginLog; import cn.enilu.material.dao.BaseRepository; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface LoginLogRepository extends BaseRepository{ } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/MenuRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Menu; import cn.enilu.material.dao.BaseRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface MenuRepository extends BaseRepository { Menu findByCode(String code); List findByPcodesLike(String code); List findByNameLikeAndLevels(String name,Integer levels); @Query(nativeQuery = true,value="SELECT m1.id AS id, m1.icon AS icon, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) " + "THEN 0 ELSE m2.id END ) AS parentId, m1. NAME AS NAME, m1.url AS url, m1.levels AS levels, m1.ismenu AS " + "ismenu, m1.num AS num,m1.code as code,m1.status as status FROM t_sys_menu m1 LEFT JOIN t_sys_menu m2 ON " + "m1.pcode = " + "m2. " + "CODE " + "INNER " + "JOIN ( SELECT ID FROM t_sys_menu WHERE ID IN ( SELECT menuid FROM t_sys_relation rela WHERE rela.roleid IN (?1))) m3 ON m1.id = m3.id WHERE m1.ismenu = 1 and m1.status = 1 ORDER BY levels, num ASC") List getMenusByRoleIds(List roleList); @Query(nativeQuery = true,value="SELECT m1.id AS id, m1.icon AS icon, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) " + "THEN 0 ELSE m2.id END ) AS parentId, m1. NAME AS NAME, m1.url AS url, m1.levels AS levels, m1.ismenu AS " + "ismenu, m1.num AS num, m1. CODE AS CODE,m1.status as status FROM t_sys_menu m1 LEFT JOIN t_sys_menu m2 " + "ON " + "m1.pcode = m2. CODE ORDER BY levels, num ASC") List getMenus(); @Query(nativeQuery = true,value="select menuid from t_sys_relation where roleid=?1") List getMenuIdsByRoleId(Integer roleId); @Query(nativeQuery = true,value = "SELECT m1.id AS id, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) THEN 0 ELSE m2.id END ) AS pId, m1. NAME AS NAME, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) THEN 'true' ELSE 'false' END ) AS isOpen, m1.code FROM t_sys_menu m1 LEFT JOIN t_sys_menu m2 ON m1.pcode = m2. CODE ORDER BY m1.id ASC") List menuTreeList(); @Query(nativeQuery = true,value = "SELECT m1.id AS id, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) THEN 0 ELSE m2.id END ) AS pId, m1. NAME AS NAME, ( CASE WHEN (m2.id = 0 OR m2.id IS NULL) THEN 'true' ELSE 'false' END ) AS isOpen, ( CASE WHEN (m3.ID = 0 OR m3.ID IS NULL) THEN 'false' ELSE 'true' END ) \"checked\" FROM t_sys_menu m1 LEFT JOIN t_sys_menu m2 ON m1.pcode = m2. CODE LEFT JOIN ( SELECT ID FROM t_sys_menu WHERE ID IN (?1)) m3 ON m1.id = m3.id ORDER BY m1.id ASC") List menuTreeListByMenuIds(List menuIds); @Modifying @Transactional @Query(nativeQuery = true,value = "delete from t_sys_relation where menuid=?1") void deleteRelationByMenu(Long menuId); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/OperationLogRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.OperationLog; import cn.enilu.material.dao.BaseRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import javax.transaction.Transactional; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface OperationLogRepository extends BaseRepository { @Modifying @Transactional @Query(nativeQuery = true,value = "delete from t_sys_operation_log") int clear(); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/RelationRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Relation; import cn.enilu.material.dao.BaseRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface RelationRepository extends BaseRepository { @Transactional @Modifying @Query(nativeQuery = true,value = "delete from t_sys_relation where roleid=?1") int deleteByRoleId(Long roleId); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/RoleRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Role; import cn.enilu.material.dao.BaseRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface RoleRepository extends BaseRepository { @Query(nativeQuery = true,value = "SELECT id, pId, NAME, ( CASE WHEN (pId = 0 OR pId IS NULL) THEN 'true' ELSE 'false' END ) OPEN FROM t_sys_role") List roleTreeList(); @Query(nativeQuery = true,value="SELECT r.id AS id, pId AS pId, NAME AS NAME, ( CASE WHEN (pId = 0 OR pId IS NULL) THEN 'true' ELSE 'false' END ) \"open\", ( CASE WHEN (r1.ID = 0 OR r1.ID IS NULL) THEN 'false' ELSE 'true' END ) AS checked FROM t_sys_role r LEFT JOIN ( SELECT ID FROM t_sys_role WHERE ID IN (?1)) r1 ON r.ID = r1.ID ORDER BY pId, num ASC") List roleTreeListByRoleId(Long[] ids); List findByName(String roleName); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/SysNoticeRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Notice; import cn.enilu.material.dao.BaseRepository; import java.util.List; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface SysNoticeRepository extends BaseRepository { List findByTitleLike(String name); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/TaskLogRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.TaskLog; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.PagingAndSortingRepository; public interface TaskLogRepository extends PagingAndSortingRepository ,JpaSpecificationExecutor ,JpaRepository { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/TaskRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.Task; import cn.enilu.material.dao.BaseRepository; import java.util.List; public interface TaskRepository extends BaseRepository { long countByNameLike(String name); List findByNameLike(String name); List findAllByDisabled(boolean disable); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/dao/system/UserRepository.java ================================================ package cn.enilu.material.dao.system; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.dao.BaseRepository; import java.util.List; /** * Created on 2018/3/21 0021. * * @author enilu */ public interface UserRepository extends BaseRepository { User findByAccount(String account); List findByRoleid(String idRole); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/factory/DictFieldWarpperFactory.java ================================================ package cn.enilu.material.factory; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.service.system.IConstantFactory; import cn.enilu.material.service.system.impl.ConstantFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Method; /** * 字段的包装创建工厂 * * @author fengshuonan * @date 2017-05-06 15:12 */ public class DictFieldWarpperFactory { private static Logger logger = LoggerFactory.getLogger(DictFieldWarpperFactory.class); public static Object createFieldWarpper(Object field, String methodName) { IConstantFactory me = ConstantFactory.me(); try { Method method = IConstantFactory.class.getMethod(methodName, field.getClass()); Object result = method.invoke(me, field); return result; } catch (Exception e) { logger.error("field:{},methodName:{}",field,methodName); try { Method method = IConstantFactory.class.getMethod(methodName, Long.class); Object result = method.invoke(me, Long.valueOf(field.toString())); return result; } catch (Exception e1) { throw new ApplicationException(BizExceptionEnum.ERROR_WRAPPER_FIELD); } } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/factory/UserFactory.java ================================================ package cn.enilu.material.factory; import cn.enilu.material.bean.dto.UserDto; import cn.enilu.material.bean.entity.system.User; import org.springframework.beans.BeanUtils; /** * 用户创建工厂 * * @author fengshuonan * @date 2017-05-05 22:43 */ public class UserFactory { public static User createUser(UserDto userDto, User user){ if(userDto == null){ return null; }else{ BeanUtils.copyProperties(userDto,user); return user; } } public static User updateUser(UserDto userDto,User user){ if(userDto == null){ return null; }else{ user.setName(userDto.getName()); user.setDeptid(userDto.getDeptid()); user.setSex(userDto.getSex()); user.setPhone(userDto.getPhone()); user.setEmail(userDto.getEmail()); user.setBirthday(userDto.getBirthday()); user.setAvatar(userDto.getAvatar()); if(userDto.getStatus()!=null){ user.setStatus(userDto.getStatus()); } return user; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/platform/package-info.java ================================================ /** * package-info * * @version 2018/9/11 0011 * @author enilu */ package cn.enilu.material.platform; ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/BaseService.java ================================================ package cn.enilu.material.service; import cn.enilu.material.bean.constant.cache.Cache; import cn.enilu.material.bean.vo.query.DynamicSpecifications; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.dao.BaseRepository; import cn.enilu.material.utils.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Map; /** * * @author :enilu * @date :Created in 2019/6/29 22:32 */ public abstract class BaseService> implements CrudService { @Autowired private R dao; @Override @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#id") public void delete(ID id) { dao.deleteById(id); } @Override public void delete(Iterable ids) { Iterator iterator = ids.iterator(); while (iterator.hasNext()) { ID id = iterator.next(); dao.deleteById(id); } } @Override public T insert(T record) { return dao.save(record); } @Override @Cacheable(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#id") public T get(ID id) { return dao.findById(id).get(); } @Override public T get(SearchFilter filter) { List list = queryAll(filter); return list.isEmpty()?null:list.get(0); } @Override public T get(List filters) { List list = queryAll(filters); return list.isEmpty()?null:list.get(0); } @Override public List query(Iterable ids) { return dao.findAllById(ids); } @Override public List queryAll() { return dao.findAll(); } @Override public Page queryPage(Page page) { Pageable pageable = null; if(page.isOpenSort()) { pageable = new PageRequest(page.getCurrent()-1, page.getSize(), page.isAsc() ? Sort.Direction.ASC : Sort.Direction.DESC, page.getOrderByField()); }else{ pageable = new PageRequest(page.getCurrent()-1,page.getSize(), Sort.Direction.DESC,"id"); } Specification specification = DynamicSpecifications.bySearchFilter(page.getFilters(),dao.getDataClass()); org.springframework.data.domain.Page pageResult = dao.findAll(specification,pageable); page.setTotal(Integer.valueOf(pageResult.getTotalElements()+"")); page.setRecords(pageResult.getContent()); return page; } @Override public List queryAll(List filters) { return queryAll(filters,null); } @Override public List queryAll(SearchFilter filter) { return queryAll(filter,null); } @Override public List queryAll(List filters, Sort sort) { Specification specification = DynamicSpecifications.bySearchFilter(filters,dao.getDataClass()); if(sort==null){ return dao.findAll(specification); } return dao.findAll(specification,sort); } @Override public List queryAll(SearchFilter filter, Sort sort) { if(filter!=null){ return queryAll(Lists.newArrayList(filter),sort); }else { return queryAll(Lists.newArrayList(), sort); } } @Override public List queryBySql(String sql){ return queryBySql(sql,Lists.newArrayList()); } @Override public List queryBySql(String sql, SearchFilter filter){ return dao.queryBySql(sql,Lists.newArrayList(filter)); } @Override public List queryBySql(String sql, List filter){ return dao.queryBySql(sql,filter); } @Override public long count(SearchFilter filter) { return count(Lists.newArrayList(filter)); } @Override public long count(List filters) { Specification specification = DynamicSpecifications.bySearchFilter(filters,dao.getDataClass()); return dao.count(specification); } @Override @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#record.id") public T update(T record) { return dao.save(record); } @Override public void clear() { dao.deleteAllInBatch(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/CrudService.java ================================================ package cn.enilu.material.service; /** * * @author :enilu * @date :Created in 2019/6/29 22:31 */ public interface CrudService extends InsertService, UpdateService, DeleteService, SelectService { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/DeleteService.java ================================================ package cn.enilu.material.service; /** * * @author :enilu * @date :Created in 2019/6/29 22:29 */ public interface DeleteService { /** * 根据主键删除记录 * * @param id 主键 */ void delete(ID id); /** * 根据主键删除记录 * * @param ids 主键集合 */ void delete(Iterable ids); /** * 清空表数据 */ void clear(); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/InsertService.java ================================================ package cn.enilu.material.service; /** * * @author :enilu * @date :Created in 2019/6/29 22:28 */ public interface InsertService { /** * 添加一条数据 * * @param record 要添加的数据 * @return 添加后生成的主键 */ T insert(T record); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/SelectService.java ================================================ package cn.enilu.material.service; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.bean.vo.query.Page; import org.springframework.data.domain.Sort; import java.util.List; import java.util.Map; /** * * @author :enilu * @date :Created in 2019/6/29 22:30 */ public interface SelectService { /** * 根据主键查询 * @param id 主键 * @return 查询结果,无结果时返回{@code null} */ T get(ID id); T get(SearchFilter filter); T get(List filters); /** * 根据多个主键查询 * @param ids 主键集合 * @return 查询结果,如果无结果返回空集合 */ List query(Iterable ids); /** * 查询所有结果 * @return 所有结果,如果无结果则返回空集合 */ List queryAll(); /** * 查询所有结果 * @return 获取分页结果 */ Page queryPage(Page page); /** * 根据多个条件查询列表数据 * @param filters * @return */ List queryAll(List filters); /** * 根据多个条件查询列表数据,并排序 * @param filters * @param sort * @return */ List queryAll(List filters, Sort sort); /** * 根据的单个条件查询列表数据 * @param filter * @return */ List queryAll(SearchFilter filter); /** * 根据的单个条件查询列表数据 * @param filter * @param sort * @return */ List queryAll(SearchFilter filter,Sort sort); /** * 根据原生sql查询数据列表, * 例:select sum(a.1+a.2) from a where a.c=:c * @param sql * @return */ List queryBySql(String sql); /** * 根据原生sql查询数据列表 * @param sql * @param filter * @return */ List queryBySql(String sql, SearchFilter filter); /** * 根据原生sql查询数据列表 * @param sql * @param filter * @return */ List queryBySql(String sql, List filter); /** * 查询记录数 * @param filter * @return */ long count(SearchFilter filter); /** * 查询记录数 * @param filters * @return */ long count(List filters); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/UpdateService.java ================================================ package cn.enilu.material.service; /** * * @author :enilu * @date :Created in 2019/6/29 22:30 */ public interface UpdateService { /** * 修改记录信息 * * @param record 要修改的对象 * @return 返回修改的记录 */ T update(T record); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/MessageService.java ================================================ package cn.enilu.material.service.message; import cn.enilu.material.bean.entity.message.Message; import cn.enilu.material.bean.entity.message.MessageSender; import cn.enilu.material.bean.entity.message.MessageTemplate; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.dao.message.MessageRepository; import cn.enilu.material.dao.message.MessagesenderRepository; import cn.enilu.material.dao.message.MessagetemplateRepository; import cn.enilu.material.service.BaseService; import cn.enilu.material.service.message.email.EmailSender; import cn.enilu.material.service.message.sms.SmsSender; import cn.enilu.material.utils.StringUtils; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import org.apache.commons.lang3.text.StrSubstitutor; import org.nutz.lang.Lang; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamSource; import org.springframework.stereotype.Service; import java.text.MessageFormat; import java.util.*; /** * MessageService * * @author enilu * @version 2019/05/17 0017 */ @Service public class MessageService extends BaseService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MessageRepository messageRepository; @Autowired private MessagesenderRepository messagesenderRepository; @Autowired private MessagetemplateRepository messagetemplateRepository; public boolean delete(String ids) { final ArrayList list = Lists.newArrayList(Splitter.on(',').split(ids)); messageRepository.deleteAllByIdIn(list); return true; } /** * 发送复杂模板的邮件 * @param tplCode * @param from * @param to * @param cc * @param title * @param dataMap */ public void sendTplEmail(String tplCode, String from, String to, String cc, String title, Map dataMap) { MessageTemplate messageTemplate = messagetemplateRepository.findByCode(tplCode); String content = getContent(messageTemplate.getContent(), dataMap); sendEmailMessage(tplCode,from,to,cc,title,content,messageTemplate,null,null); } /** * 发送带附件的邮件 * @param tplCode * @param from * @param to * @param cc * @param title * @param attachmentFilename * @param inputStreamSource * @param dataMap */ public void sendTplEmail(String tplCode, String from, String to, String cc, String title, String attachmentFilename, InputStreamSource inputStreamSource, Map dataMap) { MessageTemplate messageTemplate = messagetemplateRepository.findByCode(tplCode); String content = getContent(messageTemplate.getContent(), dataMap); sendEmailMessage(tplCode,from,to,cc,title,content,messageTemplate,attachmentFilename,inputStreamSource); } /** * 发送简单模板邮件 * @param tplCode * @param from * @param to * @param cc * @param title * @param args */ public void sendSimpleEmail(String tplCode, String from, String to, String cc, String title, String... args) { MessageTemplate messageTemplate = messagetemplateRepository.findByCode(tplCode); String content = getContent(messageTemplate.getContent(), args); sendEmailMessage(tplCode,from,to,cc,title,content,messageTemplate,null,null); } /** * 发送短信 * @param tplCode * @param receiver * @param args */ public void sendSms(String tplCode, String receiver, String... args) { MessageTemplate messageTemplate = messagetemplateRepository.findByCode(tplCode); String content = getContent(messageTemplate.getContent(), args); boolean isSuccess = false; try { isSuccess = this.sendSmsMessage(receiver, content, messageTemplate, args); }catch (Exception e){ logger.error(e.getMessage(), e); } saveMessage(0,tplCode,receiver,content,isSuccess); } private void sendEmailMessage(String tplCode, String from, String to, String cc, String title, String content,MessageTemplate messageTemplate, String attachmentFilename, InputStreamSource inputStreamSource){ try { EmailSender emailSender = getEmailSender(messageTemplate); boolean isSuccess = false; if(inputStreamSource!=null){ isSuccess = emailSender.sendEmail(from, to, cc, title, content,attachmentFilename,inputStreamSource); }else { isSuccess = emailSender.sendEmail(from, to, cc, title, content); } saveMessage(1, tplCode, to, content, isSuccess); }catch (Exception e){ logger.error(e.getMessage(), e); saveMessage(1, tplCode, to, content, false); } } private String getContent(String template, String... args) { List argList = new ArrayList<>(); argList.add(""); if (args != null) { Collections.addAll(argList, args); } String content = MessageFormat.format(template, Lang.collection2array(argList)); return content; } private String getContent(String template, Map dataMap) { return StrSubstitutor.replace(template, dataMap); } private void saveMessage(Integer type, String tplCode, String receiver, String content, Boolean sendResult) { Message message = new Message(); message.setType(type); message.setTplCode(tplCode); message.setType(0); message.setState(sendResult ? 1 : 2); message.setReceiver(receiver); message.setCreateTime(new Date()); message.setContent(content); messageRepository.save(message); } private boolean sendSmsMessage( String receiver, String content, MessageTemplate messageTemplate,String... args) throws Exception { String tplCode = getTpl(messageTemplate); SmsSender smsSender = getSmsSender(messageTemplate); boolean success = false; String[] receivers = receiver.split(",|;", -1); for (String oneReceiver : receivers) { try { if (StringUtils.isNotEmpty(oneReceiver)) { success = smsSender.sendSms(tplCode, oneReceiver, args, content); } } catch (Exception e) { logger.error(e.getMessage(), e); } } return success; } private SmsSender getSmsSender(MessageTemplate messageTemplate) throws Exception { MessageSender messageSender = messagesenderRepository.findById(messageTemplate.getIdMessageSender()).get(); if (messageSender != null) { try { return SpringContextHolder.getBean(messageSender.getClassName()); } catch (Exception e) { logger.error("获取SmsService实现类失败", e); throw new Exception("smsService名称配置失败:" + messageSender.getClassName()); } } else { throw new Exception("未配置运营商模版id"); } } private EmailSender getEmailSender(MessageTemplate messageTemplate) throws Exception { MessageSender messageSender = messagesenderRepository.findById(messageTemplate.getIdMessageSender()).get(); if (messageSender != null) { try { return SpringContextHolder.getBean(messageSender.getClassName()); } catch (Exception e) { logger.error("获取SmsService实现类失败", e); throw new Exception("smsService名称配置失败:" + messageSender.getClassName()); } } else { throw new Exception("未配置运营商模版id"); } } private String getTpl(MessageTemplate messageTemplate) { MessageSender messageSender = messagesenderRepository.findById(messageTemplate.getIdMessageSender()).get(); if (messageSender != null && StringUtils.isNotEmpty(messageSender.getTplCode())) { return messageSender.getTplCode(); } else { return null; } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/MessagesenderService.java ================================================ package cn.enilu.material.service.message; import cn.enilu.material.bean.entity.message.MessageSender; import cn.enilu.material.bean.entity.message.MessageTemplate; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.dao.message.MessagesenderRepository; import cn.enilu.material.dao.message.MessagetemplateRepository; import cn.enilu.material.service.BaseService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * MessagesenderService * * @author enilu * @version 2019/05/17 0017 */ @Service public class MessagesenderService extends BaseService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MessagesenderRepository messageSenderRepository; @Autowired private MessagetemplateRepository messagetemplateRepository; public void save(MessageSender messageSender){ messageSenderRepository.save(messageSender); } @Override public void delete(Long id) throws ApplicationException { List templateList = messagetemplateRepository.findByIdMessageSender(id); if(templateList.isEmpty()) { messageSenderRepository.deleteById(id); }else{ throw new ApplicationException(BizExceptionEnum.CAN_NOT_DELETE); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/MessagetemplateService.java ================================================ package cn.enilu.material.service.message; import cn.enilu.material.bean.entity.message.MessageTemplate; import cn.enilu.material.dao.message.MessagetemplateRepository; import cn.enilu.material.service.BaseService; import org.springframework.stereotype.Service; /** * MessagetemplateService * * @author enilu * @version 2019/05/17 0017 */ @Service public class MessagetemplateService extends BaseService { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/email/DefaultEmailSender.java ================================================ package cn.enilu.material.service.message.email; import cn.enilu.material.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamSource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import javax.mail.internet.MimeMessage; /** * 默认的邮件发送服务类 * * @author :enilu * @date :Created in 2019/6/11 15:19 */ @Service public class DefaultEmailSender implements EmailSender{ @Autowired private JavaMailSender javaMailSender; @Override public boolean sendEmail(String from, String to, String cc, String title, String content){ return sendEmail(from,to,cc,title,content,null,null); } @Override public boolean sendEmail(String from, String to, String cc, String title, String content, String attachmentFilename, InputStreamSource inputStreamSource) { MimeMessage message = null; try { message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); if(StringUtils.isNotEmpty(cc)) { helper.setCc(cc); } helper.setSubject(title); helper.setText(content, true); if(inputStreamSource!=null) { helper.addAttachment(attachmentFilename, inputStreamSource); } javaMailSender.send(message); return true; } catch (Exception e) { e.printStackTrace(); } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/email/EmailSender.java ================================================ package cn.enilu.material.service.message.email; import org.springframework.core.io.InputStreamSource; /** * 邮件发送接口 * * @author :enilu * @date :Created in 2019/6/17 19:33 */ public interface EmailSender { /** * 发送邮件 * @param from * @param to * @param cc * @param title * @param content * @return */ boolean sendEmail(String from, String to, String cc, String title, String content); /** * 发送带附件的邮件 * @param from * @param to * @param cc * @param title * @param content * @param attachmentFilename * @param inputStreamSource * @return */ boolean sendEmail(String from, String to, String cc, String title, String content, String attachmentFilename, InputStreamSource inputStreamSource); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/sms/SmsSender.java ================================================ package cn.enilu.material.service.message.sms; public interface SmsSender { /** * 发送短信,如果内容content不为空,则直接发送内容 * @param tplCode 短信运营商模板号码 * @param receiver * @param args * @param content * @return */ boolean sendSms(String tplCode, String receiver, String[] args, String content); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/message/sms/tencent/TencentSmsSender.java ================================================ package cn.enilu.material.service.message.sms.tencent; import cn.enilu.material.bean.enumeration.ConfigKeyEnum; import cn.enilu.material.dao.cache.ConfigCache; import cn.enilu.material.service.message.sms.SmsSender; import cn.enilu.material.utils.StringUtils; import com.github.qcloudsms.SmsSingleSender; import com.github.qcloudsms.SmsSingleSenderResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class TencentSmsSender implements SmsSender { private Logger logger = LoggerFactory.getLogger(TencentSmsSender.class); @Autowired private ConfigCache configCache; @Override public boolean sendSms(String tplCode, String receiver, String[] args, String content) { Integer appid = Integer.valueOf((String) configCache.get(ConfigKeyEnum.API_TENCENT_SMS_APPID.getValue())); String appkey = (String) configCache.get(ConfigKeyEnum.API_TENCENT_SMS_APPKEY.getValue()); String smsSign = (String) configCache.get(ConfigKeyEnum.API_TENCENT_SMS_SIGN.getValue()); SmsSingleSender ssender = new SmsSingleSender(appid, appkey); SmsSingleSenderResult result = null; try{ if(StringUtils.isNotEmpty(tplCode)){ //根据指定模板id发送短信 // 签名参数未提供或者为空时,会使用默认签名发送短信 result = ssender.sendWithParam("86", receiver, Integer.valueOf(tplCode), args, smsSign, "", ""); }else { //发送固定内容短信 result = ssender.send(0, "86", receiver, content, "", ""); } logger.info(result.errMsg); return result.result == 0; } catch (Exception e) { logger.error("发送短信异常",e); } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/AccountService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.dao.cache.TokenCache; import cn.enilu.material.platform.log.LogManager; import cn.enilu.material.platform.log.LogTaskFactory; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.Convert; import cn.enilu.material.utils.HttpKit; import com.google.common.collect.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.UUID; /** * AccountService * * @author enilu * @version 2018/9/12 0012 */ @Service public class AccountService { @Autowired private TokenCache tokenCache; @Autowired private UserService userService; public String login(Long idUser) { String token = UUID.randomUUID().toString(); tokenCache.put(token, idUser); LogManager.me().executeLog(LogTaskFactory.loginLog(idUser, HttpKit.getIp())); User user = userService.get(idUser); Long[] roleArray = Convert.toLongArray(true,Convert.toStrArray(",", user.getRoleid())); ShiroUser shiroUser = new ShiroUser(); shiroUser.setAccount(user.getAccount()); shiroUser.setDeptId(user.getDeptid()); shiroUser.setDeptName(ConstantFactory.me().getDeptName(user.getDeptid())); shiroUser.setId(idUser); shiroUser.setName(user.getName()); shiroUser.setRoleList(Lists.newArrayList(roleArray)); List roleNames = Lists.newArrayList(); List roleCodes = Lists.newArrayList(); for (Long roleId : roleArray) { roleCodes.add(ConstantFactory.me().getSingleRoleTip(roleId)); roleNames.add(ConstantFactory.me().getSingleRoleName(roleId)); } shiroUser.setRoleNames(roleNames); shiroUser.setRoleCodes(roleCodes); tokenCache.setUser(token,shiroUser); return token; } public void logout(String token) { tokenCache.remove(token); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/CfgService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Cfg; import cn.enilu.material.dao.cache.ConfigCache; import cn.enilu.material.dao.system.CfgRepository; import cn.enilu.material.service.BaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * CfgService * * @author enilu * @version 2018/11/17 0017 */ @Service public class CfgService extends BaseService { @Autowired private ConfigCache configCache; public Cfg saveOrUpdate(Cfg cfg) { if(cfg.getId()==null){ insert(cfg); }else{ update(cfg); } configCache.cache(); return cfg; } @Override public void delete(Long id) { super.delete(id); configCache.cache(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/DeptService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Dept; import cn.enilu.material.bean.vo.node.DeptNode; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.dao.system.DeptRepository; import cn.enilu.material.service.BaseService; import cn.enilu.material.utils.ToolUtil; import com.google.common.base.Strings; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * Created on 2018/3/21 0021. * * @author enilu */ @Service public class DeptService extends BaseService { @Autowired private DeptRepository deptRepository; public List tree() { List list = deptRepository.tree(); List nodes = new ArrayList<>(); for(Object[] obj:list){ ZTreeNode node = transfer(obj); nodes.add(node); } return nodes; } private ZTreeNode transfer(Object[] obj){ ZTreeNode node = new ZTreeNode(); node.setId(Long.valueOf(obj[0].toString())); node.setpId(Long.valueOf(obj[1].toString())); node.setName(obj[2].toString()); node.setIsOpen(Boolean.valueOf(obj[3].toString())); return node; } public List query(String condition) { List list = new ArrayList<>(); if(Strings.isNullOrEmpty(condition)){ list = (List) deptRepository.findAll(); }else{ condition = "%"+condition+"%"; list = deptRepository.findBySimplenameLikeOrFullnameLike(condition,condition); } return list; } public void deleteDept(Long deptId) { Dept dept = get(deptId); List subDepts = deptRepository.findByPidsLike("%[" + dept.getId() + "]%"); deptRepository.deleteAll(subDepts); deptRepository.delete(dept); } public List queryAllNode() { List list = queryAll(); return generateTree(list); } public void deptSetPids(Dept dept) { if (ToolUtil.isEmpty(dept.getPid()) || dept.getPid().equals(0)) { dept.setPid(0L); dept.setPids("[0],"); } else { Long pid = dept.getPid(); Dept temp = get(pid); String pids = ""; if(temp!=null){ pids = temp.getPids(); } dept.setPid(pid); dept.setPids(pids + "[" + pid + "],"); } } private List generateTree(List list){ List nodes = new ArrayList<>(20); for(Dept dept:list){ DeptNode deptNode = new DeptNode(); BeanUtils.copyProperties(dept,deptNode); nodes.add(deptNode); } for(DeptNode deptNode:nodes){ for(DeptNode child:nodes){ if(child.getPid().intValue() == deptNode.getId().intValue()){ deptNode.getChildren().add(child); } } } List result = new ArrayList<>(20); for(DeptNode node:nodes){ if(node.getPid().intValue() == 0){ result.add(node); } } return result; } public Dept get(Long id) { Optional optiona = deptRepository.findById(id); if (optiona.isPresent()) { return optiona.get(); } return null; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/DictService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Dict; import cn.enilu.material.dao.cache.DictCache; import cn.enilu.material.dao.system.DictRepository; import cn.enilu.material.bean.vo.query.MutiStrFactory; import cn.enilu.material.service.BaseService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Optional; /** * 字典服务 * * @author fengshuonan * @date 2017-04-27 17:00 */ @Service public class DictService extends BaseService { private Logger logger = LoggerFactory.getLogger(DictService.class); @Resource DictRepository dictRepository; @Autowired private DictCache dictCache; public void addDict(String dictName, String dictValues) { //判断有没有该字典 List dicts = dictRepository.findByNameAndPid(dictName,0L); if(dicts != null && dicts.size() > 0){ return ; } //解析dictValues List> items = MutiStrFactory.parseKeyValue(dictValues); //添加字典 Dict dict = new Dict(); dict.setName(dictName); dict.setValue("0"); dict.setPid(0L); this.dictRepository.save(dict); //添加字典条目 for (Map item : items) { String val = item.get(MutiStrFactory.MUTI_STR_KEY); String name = item.get(MutiStrFactory.MUTI_STR_VALUE); Dict itemDict = new Dict(); itemDict.setPid(dict.getId()); itemDict.setName(name); try { itemDict.setValue(val); }catch (NumberFormatException e){ logger.error(e.getMessage(),e); } this.dictRepository.save(itemDict); } dictCache.cache(); } public void editDict(Long dictId, String dictName, String dicts) { //删除之前的字典 this.delteDict(dictId); //重新添加新的字典 this.addDict(dictName,dicts); dictCache.cache(); } public void delteDict(Long dictId) { //删除这个字典的子词典 List subList = dictRepository.findByPid(dictId); dictRepository.deleteAll(subList); //删除这个词典 dictRepository.deleteById(dictId); dictCache.cache(); } public Dict get(Long id) { Optional optional = dictRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } public List queryByPid(Long pid) { return dictRepository.findByPid(pid); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/FileService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.FileInfo; import cn.enilu.material.bean.enumeration.ConfigKeyEnum; import cn.enilu.material.dao.cache.ConfigCache; import cn.enilu.material.dao.cache.TokenCache; import cn.enilu.material.dao.system.FileInfoRepository; import cn.enilu.material.utils.StringUtils; import cn.enilu.material.bean.vo.query.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.io.File; import java.util.*; @Service public class FileService { @Autowired private ConfigCache configCache; @Autowired private FileInfoRepository fileInfoRepository; @Autowired private TokenCache tokenCache; public FileInfo save(MultipartFile multipartFile){ String uuid = UUID.randomUUID().toString(); String realFileName = uuid +"."+ multipartFile.getOriginalFilename().split("\\.")[1]; try { File file = new File(configCache.get(ConfigKeyEnum.SYSTEM_FILE_UPLOAD_PATH.getValue()) + File.separator+realFileName); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } multipartFile.transferTo(file); FileInfo fileInfo = new FileInfo(); fileInfo.setCreateTime(new Date()); fileInfo.setCreateBy(tokenCache.getIdUser()); fileInfo.setOriginalFileName(multipartFile.getOriginalFilename()); fileInfo.setRealFileName(realFileName); fileInfoRepository.save(fileInfo); return fileInfo; } catch (Exception e) { e.printStackTrace(); return null; } } public FileInfo get(Long id){ FileInfo fileInfo = fileInfoRepository.getOne(id); fileInfo.setAblatePath(configCache.get(ConfigKeyEnum.SYSTEM_FILE_UPLOAD_PATH.getValue()) + File.separator+fileInfo.getRealFileName()); return fileInfo; } public Page findPage(Page page, HashMap params) { Pageable pageable = new PageRequest(page.getCurrent() - 1, page.getSize(), Sort.Direction.DESC,"id"); org.springframework.data.domain.Page pageResult = fileInfoRepository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { List list = new ArrayList(); if (StringUtils.isNotEmpty(params.get("originalFileName"))) { list.add(criteriaBuilder.like(root.get("originalFileName").as(String.class), "%" + params.get("originalFileName") + "%")); } Predicate[] p = new Predicate[list.size()]; return criteriaBuilder.and(list.toArray(p)); } }, pageable); page.setTotal(Integer.valueOf(pageResult.getTotalElements() + "")); page.setRecords(pageResult.getContent()); return page; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/IConstantFactory.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.*; import cn.enilu.material.bean.vo.DictVo; import java.util.List; /** * 常量生产工厂的接口 * * @author fengshuonan * @date 2017-06-14 21:12 */ public interface IConstantFactory { /** * 获取静态资源版本号 * @return */ String resourceVersion(); /** * 根据用户id获取用户名称 * * @author enilu.cn * @Date 2017/5/9 23:41 */ String getUserNameById(Long userId); /** * 根据用户id获取用户账号 * * @author enilu.cn * @date 2017年5月16日21:55:371 */ String getUserAccountById(Long userId); /** * 通过角色ids获取角色名称 */ String getRoleName(String roleIds); /** * 通过角色id获取角色名称 */ String getSingleRoleName(Long roleId); /** * 通过角色id获取角色英文名称 */ String getSingleRoleTip(Long roleId); /** * 获取部门名称 */ String getDeptName(Long deptId); /** * 获取菜单的名称们(多个) */ String getMenuNames(String menuIds); /** * 获取菜单名称 */ String getMenuName(Long menuId); /** * 获取菜单名称通过编号 */ String getMenuNameByCode(String code); /** * 根据字典名称获取字典列表 * @param dictName * @return */ List findByDictName(String dictName); /** * 获取字典名称 */ String getDictName(Long dictId); /** * 获取通知标题 */ String getNoticeTitle(Long dictId); /** * 根据字典名称和字典中的值获取对应的名称 */ String getDictsByName(String name, String val); /** * 获取性别名称 */ String getSexName(Integer sex); /** * 获取银行卡类型名称 * @param cardType * @return */ String getCardTypeName(String cardType); /** * 获取个人证件类型 * @param cardType * @return */ String getIdCardTypeName(String cardType); /** * 获取联系人关系 * @param relation * @return */ String getRelationName(String relation); /** * 获取用户登录状态 */ String getStatusName(Integer status); /** * 获取菜单状态 */ String getMenuStatusName(Integer status); /** * 查询字典 */ List findInDict(Long id); /** * 获取被缓存的对象(用户删除业务) */ String getCacheObject(String para); /** * 获取子部门id */ List getSubDeptId(Long deptid); /** * 获取所有父部门id */ List getParentDeptIds(Long deptid); /** * 获取指定名称下的字典列表 * @param pname * @return */ List getDicts(String pname); /** * 获取全局参数 * @param cfgName * @return */ String getCfg(String cfgName); Role getRole(Long id) ; Dept getDept(Long id); Menu getMenu(Long id) ; Notice getNotice(Long id); void cleanLocalCache() ; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/LogObjectHolder.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.vo.SpringContextHolder; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.web.context.WebApplicationContext; import java.io.Serializable; /** * 被修改的bean临时存放的地方 * * @author fengshuonan * @date 2017-03-31 11:19 */ @Component @Scope(scopeName = WebApplicationContext.SCOPE_SESSION) public class LogObjectHolder implements Serializable{ private Object object = null; public void set(Object obj) { this.object = obj; } public Object get() { return object; } public static LogObjectHolder me(){ LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class); return bean; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/LoginLogService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.LoginLog; import cn.enilu.material.dao.system.LoginLogRepository; import cn.enilu.material.service.BaseService; import org.springframework.stereotype.Service; /** * Created on 2018/3/26 0026. * * @author enilu */ @Service public class LoginLogService extends BaseService { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/MenuService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Menu; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.vo.node.MenuNode; import cn.enilu.material.bean.vo.node.Node; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.dao.system.MenuRepository; import cn.enilu.material.service.BaseService; import cn.enilu.material.utils.ToolUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; /** * Created on 2018/3/23 0023. * * @author enilu */ @Service public class MenuService extends BaseService { private Logger logger = LoggerFactory.getLogger(MenuService.class); @Autowired private MenuRepository menuRepository; public void delMenu(Long menuId) { //删除菜单 delete(menuId); //删除关联的relation menuRepository.deleteRelationByMenu(menuId); } public void delMenuContainSubMenus(Long menuId) { Menu menu = get(menuId); //删除所有子菜单 List menus = menuRepository.findByPcodesLike("%[" + menu.getCode() + "]%"); menuRepository.deleteAll(menus); //删除当前菜单 delMenu(menuId); } public List getMenusByRoleIds(List roleList) { List menus = menuRepository.getMenusByRoleIds(roleList); return transferMenuNode(menus); } public List getMenusTreeByRoleIds(List roleList) { List menus = menuRepository.getMenusByRoleIds(roleList); List result = generateTree(transferMenuNode(menus)); for(MenuNode menuNode:result){ if(!menuNode.getChildren().isEmpty()){ sortTree(menuNode.getChildren()); for(MenuNode menuNode1: menuNode.getChildren()){ if(!menuNode1.getChildren().isEmpty()) { sortTree(menuNode1.getChildren()); } } } } sortTree(result); return result; } public List getMenus() { List list = transferMenuNode(menuRepository.getMenus()); List result = generateTree(list); for(MenuNode menuNode:result){ if(!menuNode.getChildren().isEmpty()){ sortTree(menuNode.getChildren()); for(MenuNode menuNode1: menuNode.getChildren()){ if(!menuNode1.getChildren().isEmpty()) { sortTree(menuNode1.getChildren()); } } } } sortTree(result); return result; } private void sortTree(List list){ Collections.sort(list, new Comparator() { @Override public int compare(MenuNode o1, MenuNode o2) { return o1.getNum()-o2.getNum(); } }); } private List generateTree(List list){ List result = new ArrayList<>(20); Map map = cn.enilu.material.utils.Lists.toMap(list,"id"); for(Map.Entry entry:map.entrySet()){ MenuNode menuNode = entry.getValue(); if(menuNode.getParentId().intValue()!=0){ MenuNode parentNode = map.get(menuNode.getParentId()); parentNode.getChildren().add(menuNode); }else{ result.add(menuNode); } } return result; } private List transferMenuNode(List menus){ List menuNodes = new ArrayList<>(); try { for(int i=0;i menuTreeList() { List list = menuRepository.menuTreeList(); List nodes =new ArrayList<>(); for(int i=0;i menuTreeListByMenuIds(List menuIds) { List list = menuRepository.menuTreeListByMenuIds(menuIds); List nodes =new ArrayList<>(); for(int i=0;i generateMenuTreeForRole(List list){ List nodes = new ArrayList<>(20); for(ZTreeNode menu:list){ Node permissionNode = new Node(); permissionNode.setId(menu.getId()); permissionNode.setName(menu.getName()); permissionNode.setPid(menu.getpId()); permissionNode.setChecked(menu.getChecked()); nodes.add(permissionNode); } for(Node permissionNode:nodes){ for(Node child:nodes){ if(child.getPid().intValue() == permissionNode.getId().intValue()){ permissionNode.getChildren().add(child); } } } List result = new ArrayList<>(20); for(Node node:nodes){ if(node.getPid().intValue() == 0){ result.add(node); } } return result; } public Menu get(Long id) { Optional optiona = menuRepository.findById(id); if (optiona.isPresent()) { return optiona.get(); } return null; } public Menu findByCode(String code) { return menuRepository.findByCode(code); } public List findByNameLike(String name) { return queryAll(SearchFilter.build("name", SearchFilter.Operator.LIKE,name)); } public List findByLevels(Integer level) { return queryAll(SearchFilter.build("levels",level)); } public List getMenuIdsByRoleId(Integer roleId) { return menuRepository.getMenuIdsByRoleId(roleId); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/NoticeService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Notice; import cn.enilu.material.dao.system.SysNoticeRepository; import cn.enilu.material.service.BaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @author :enilu * @date :Created in 2019/7/21 21:29 */ @Service public class NoticeService extends BaseService { @Autowired private SysNoticeRepository sysNoticeRepository; public List findByTitleLike(String title) { return sysNoticeRepository.findByTitleLike(title); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/OperationLogService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.OperationLog; import cn.enilu.material.dao.system.OperationLogRepository; import cn.enilu.material.service.BaseService; import org.springframework.stereotype.Service; /** * Created on 2018/3/26 0026. * * @author enilu */ @Service public class OperationLogService extends BaseService { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/RoleService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.entity.system.Relation; import cn.enilu.material.bean.entity.system.Role; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.exception.ExceptionEnum; import cn.enilu.material.bean.vo.node.Node; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.dao.system.RelationRepository; import cn.enilu.material.dao.system.RoleRepository; import cn.enilu.material.dao.system.UserRepository; import cn.enilu.material.service.BaseService; import cn.enilu.material.utils.Convert; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * Created on 2018/3/25 0025. * * @author enilu */ @Service public class RoleService extends BaseService { @Autowired private RoleRepository roleRepository; @Autowired private RelationRepository relationRepository; @Autowired private UserRepository userRepository; public List roleTreeList() { List list = roleRepository.roleTreeList(); List treeNodes = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { Object[] arr = (Object[]) list.get(i); ZTreeNode node = new ZTreeNode(); node.setId(Long.valueOf(arr[0].toString())); node.setpId(Long.valueOf(arr[1].toString())); node.setName(arr[2].toString()); node.setOpen(Boolean.valueOf(arr[3].toString())); treeNodes.add(node); } return treeNodes; } public List roleTreeListByRoleId(Long[] ids) { List list = roleRepository.roleTreeListByRoleId(ids); List treeNodes = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { Object[] arr = (Object[]) list.get(i); ZTreeNode node = new ZTreeNode(); node.setId(Long.valueOf(arr[0].toString())); node.setpId(Long.valueOf(arr[1].toString())); node.setName(arr[2].toString()); node.setOpen(Boolean.valueOf(arr[3].toString())); node.setChecked(Boolean.valueOf(arr[4].toString())); treeNodes.add(node); } return treeNodes; } public void setAuthority(Long roleId, String ids) { // 删除该角色所有的权限 relationRepository.deleteByRoleId(roleId); // 添加新的权限 for (Long id : Convert.toLongArray(true, Convert.toStrArray(",", ids))) { Relation relation = new Relation(); relation.setRoleid(roleId); relation.setMenuid(id); relationRepository.save(relation); } } public void delRoleById(Long roleId) { List list = userRepository.findByRoleid(String.valueOf(roleId)); if(!list.isEmpty()){ throw new ApplicationException(BizExceptionEnum.NOT_ALLOW); } //删除角色 roleRepository.deleteById(roleId); // 删除该角色所有的权限 relationRepository.deleteByRoleId(roleId); } public List generateRoleTree(List list) { List nodes = new ArrayList<>(); for (ZTreeNode role : list) { Node roleNode = new Node(); roleNode.setId(role.getId()); roleNode.setName(role.getName()); roleNode.setPid(role.getpId()); roleNode.setChecked(role.getChecked()); nodes.add(roleNode); } return nodes; } public Role get(Long id) { Optional optional = roleRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } public List findByName(String roleName) { return roleRepository.findByName(roleName); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/UserService.java ================================================ package cn.enilu.material.service.system; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.dao.system.UserRepository; import cn.enilu.material.service.BaseService; import cn.enilu.material.utils.DateUtil; import com.google.common.base.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; /** * Created on 2018/3/23 0023. * * @author enilu */ @Service public class UserService extends BaseService { @Autowired private UserRepository userRepository; public List findAll(final Map params) { return userRepository.findAll(new Specification(){ @Override public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { List list = new ArrayList(); if(params.get("deptid") !=null && !Strings.isNullOrEmpty(params.get("deptid").toString())){ list.add(criteriaBuilder.equal(root.get("deptid").as(String.class), params.get("deptid").toString())); } if(params.get("name") !=null && !Strings.isNullOrEmpty(params.get("name").toString())){ Predicate p1 = criteriaBuilder.like(root.get("name").as(String.class), params.get("name").toString()); Predicate p2 = criteriaBuilder.like(root.get("account").as(String.class),params.get("name").toString()); list.add(criteriaBuilder.or(p1,p2)); } list.add(criteriaBuilder.notEqual(root.get("id").as(Long.class), Const.SYSTEM_USER_ID)); if(params.get("beginTime") != null && !Strings.isNullOrEmpty(params.get("beginTime").toString())){ list.add(criteriaBuilder.greaterThan(root.get("createTime").as(Date.class), DateUtil.parseDate(params.get("beginTime").toString()))); } if(params.get("endTime") != null && !Strings.isNullOrEmpty(params.get("endTime").toString())){ list.add(criteriaBuilder.lessThan(root.get("createTime").as(Date.class),DateUtil.parseDate(params.get("endTime").toString()))); } Predicate[] p = new Predicate[list.size()]; return criteriaBuilder.and(list.toArray(p)); } }); } public User findByAccount(String account) { return userRepository.findByAccount(account); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/system/impl/ConstantFactory.java ================================================ package cn.enilu.material.service.system.impl; import cn.enilu.material.bean.constant.cache.CacheKey; import cn.enilu.material.bean.constant.state.ManagerStatus; import cn.enilu.material.bean.constant.state.MenuStatus; import cn.enilu.material.bean.entity.system.*; import cn.enilu.material.bean.enumeration.ConfigKeyEnum; import cn.enilu.material.bean.vo.DictVo; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.dao.cache.ConfigCache; import cn.enilu.material.dao.cache.DictCache; import cn.enilu.material.dao.system.*; import cn.enilu.material.service.system.IConstantFactory; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.utils.Convert; import cn.enilu.material.utils.StrKit; import cn.enilu.material.utils.StringUtils; import cn.enilu.material.utils.cache.TimeCacheMap; import org.springframework.cache.annotation.CacheConfig; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * 常量的生产工厂 * * @author fengshuonan * @date 2017年2月13日 下午10:55:21 */ @Component @DependsOn("springContextHolder") @CacheConfig public class ConstantFactory implements IConstantFactory { public static TimeCacheMap cache = new TimeCacheMap(3600, 2); private RoleRepository roleRepository = SpringContextHolder.getBean(RoleRepository.class); private DeptRepository deptRepository = SpringContextHolder.getBean(DeptRepository.class); private DictCache dictCache = SpringContextHolder.getBean(DictCache.class); private DictRepository dictRepository = SpringContextHolder.getBean(DictRepository.class); private UserRepository userRepository = SpringContextHolder.getBean(UserRepository.class); private MenuRepository menuRepository = SpringContextHolder.getBean(MenuRepository.class); private SysNoticeRepository sysNoticeRepository = SpringContextHolder.getBean(SysNoticeRepository.class); private ConfigCache configCache = SpringContextHolder.getBean(ConfigCache.class); public static IConstantFactory me() { return SpringContextHolder.getBean("constantFactory"); } public String get(String key) { return cache.get(key); } public void set(String key, String val) { cache.put(key, val); } @Override public String resourceVersion(){ return (String) configCache.get(ConfigKeyEnum.SYSTEM_RESOURCE_VERSION.getValue()); } /** * 根据用户id获取用户名称 * * @author enilu.cn * @Date 2017/5/9 23:41 */ @Override public String getUserNameById(Long userId) { String val = get(CacheKey.SYS_USER_NAME + userId); if (StringUtils.isNotEmpty(val)) { return val; } User user = getUser(userId); if (user != null) { val = user.getName(); set(CacheKey.SYS_USER_NAME + userId, val); return val; } return "--"; } private User getUser(Long id) { Optional optionalUser = userRepository.findById(id); if (optionalUser.isPresent()) { User user = optionalUser.get(); return user; } return null; } /** * 根据用户id获取用户账号 * * @author enilu.cn * @date 2017年5月16日21:55:371 */ @Override public String getUserAccountById(Long userId) { User user = getUser(userId); if (user != null) { return user.getAccount(); } else { return "--"; } } /** * 通过角色ids获取角色名称 */ @Override public String getRoleName(String roleIds) { String val = get(CacheKey.ROLES_NAME + roleIds); if (StringUtils.isNotEmpty(val)) { return val; } Integer[] roles = Convert.toIntArray(roleIds); StringBuilder sb = new StringBuilder(); for (Integer role : roles) { Role roleObj = getRole(Long.valueOf(role)); if (StringUtils.isNotNullOrEmpty(roleObj) && StringUtils.isNotEmpty(roleObj.getName())) { sb.append(roleObj.getName()).append(","); } } val = StrKit.removeSuffix(sb.toString(), ","); set(CacheKey.ROLES_NAME + roleIds, val); return val; } /** * 通过角色id获取角色名称 */ @Override public String getSingleRoleName(Long roleId) { if (0 == roleId) { return "--"; } Role roleObj = getRole(roleId); if (StringUtils.isNotNullOrEmpty(roleObj) && StringUtils.isNotEmpty(roleObj.getName())) { return roleObj.getName(); } return ""; } /** * 通过角色id获取角色英文名称 */ @Override public String getSingleRoleTip(Long roleId) { if (0 == roleId) { return "--"; } Role roleObj = getRole(roleId); if (StringUtils.isNotNullOrEmpty(roleObj) && StringUtils.isNotEmpty(roleObj.getName())) { return roleObj.getTips(); } return ""; } /** * 获取部门名称 */ @Override public String getDeptName(Long deptId) { if (deptId == null) { return null; } String val = get(CacheKey.DEPT_NAME + deptId); if (StringUtils.isNotEmpty(val)) { return val; } Dept dept = getDept(deptId); if (StringUtils.isNotNullOrEmpty(dept) && StringUtils.isNotEmpty(dept.getFullname())) { val = dept.getFullname(); set(CacheKey.DEPT_NAME + deptId, val); return val; } return ""; } /** * 获取菜单的名称们(多个) */ @Override public String getMenuNames(String menuIds) { Integer[] menuArray = Convert.toIntArray(menuIds); StringBuilder sb = new StringBuilder(); for (int menuId : menuArray) { Menu menuObj = getMenu(Long.valueOf(menuId)); if (StringUtils.isNotNullOrEmpty(menuObj) && StringUtils.isNotEmpty(menuObj.getName())) { sb.append(menuObj.getName()).append(","); } } return StrKit.removeSuffix(sb.toString(), ","); } /** * 获取菜单名称 */ @Override public String getMenuName(Long menuId) { Menu menu = getMenu(menuId); if (menu == null) { return ""; } else { return menu.getName(); } } /** * 获取菜单名称通过编号 */ @Override public String getMenuNameByCode(String code) { Menu menu = menuRepository.findByCode(code); if (menu == null) { return ""; } else { return menu.getName(); } } @Override public List findByDictName(String dictName) { List list = new ArrayList(); List dicts = dictCache.getDictsByPname(dictName); if (dicts != null) { for (int i = 0; i < dicts.size(); i++) { Dict dict = dicts.get(i); DictVo dictVo = new DictVo(dict.getValue(), dict.getName()); list.add(dictVo); } } return list; } /** * 获取字典名称 */ @Override public String getDictName(Long dictId) { String val = get(CacheKey.DICT_NAME + dictId); if (StringUtils.isNotEmpty(val)) { return val; } val = dictCache.getDict(dictId); set(CacheKey.DICT_NAME + dictId, val); return val; } /** * 获取通知标题 */ @Override public String getNoticeTitle(Long id) { Notice notice = getNotice(id); if (notice == null) { return ""; } else { return notice.getTitle(); } } /** * 根据字典名称和字典中的值获取对应的名称 */ @Override public String getDictsByName(String name, String val) { String result = get(CacheKey.DICT_NAME + name + val); if (StringUtils.isNotEmpty(result)) { return result; } List dicts = dictCache.getDictsByPname(name); for (Dict item : dicts) { if (item.getValue() != null && item.getValue().equals(val)) { result = item.getName(); set(CacheKey.DICT_NAME + name + val, result); return result; } } return ""; } /** * 获取性别名称 */ @Override public String getSexName(Integer sex) { return getDictsByName("性别", String.valueOf(sex)); } @Override public String getCardTypeName(String cardType) { return getDictsByName("银行卡类型", cardType); } @Override public String getIdCardTypeName(String cardType) { return getDictsByName("证件类型", cardType); } @Override public String getRelationName(String relation) { return getDictsByName("联系人关系", relation); } /** * 获取用户登录状态 */ @Override public String getStatusName(Integer status) { return ManagerStatus.valueOf(status); } /** * 获取菜单状态 */ @Override public String getMenuStatusName(Integer status) { return MenuStatus.valueOf(status); } /** * 查询字典 */ @Override public List findInDict(Long id) { return dictRepository.findByPid(id); } /** * 获取被缓存的对象(用户删除业务) */ @Override public String getCacheObject(String para) { return LogObjectHolder.me().get().toString(); } /** * 获取子部门id */ @Override public List getSubDeptId(Long deptid) { List depts = this.deptRepository.findByPidsLike("%[" + deptid + "]%"); ArrayList deptids = new ArrayList<>(); if (depts != null && depts.size() > 0) { for (Dept dept : depts) { deptids.add(dept.getId()); } } return deptids; } /** * 获取所有父部门id */ @Override public List getParentDeptIds(Long deptid) { Dept dept = getDept(deptid); String pids = dept.getPids(); String[] split = pids.split(","); ArrayList parentDeptIds = new ArrayList<>(); for (String s : split) { parentDeptIds.add(Integer.valueOf(StrKit.removeSuffix(StrKit.removePrefix(s, "["), "]"))); } return parentDeptIds; } @Override public List getDicts(String pname) { return dictCache.getDictsByPname(pname); } @Override public String getCfg(String cfgName) { String val = get(CacheKey.CFG + cfgName); if (StringUtils.isNotEmpty(val)) { return val; } val = (String) configCache.get(cfgName); set(CacheKey.CFG + cfgName, val); return val; } @Override public Role getRole(Long id) { Optional optional = roleRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } @Override public Dept getDept(Long id) { Optional optional = deptRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } @Override public Menu getMenu(Long id) { Optional optiona = menuRepository.findById(id); if (optiona.isPresent()) { return optiona.get(); } return null; } @Override public Notice getNotice(Long id) { Optional optional = sysNoticeRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } @Override public void cleanLocalCache() { cache.cleanup(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/BaseJob.java ================================================ package cn.enilu.material.service.task; import cn.enilu.material.bean.vo.QuartzJob; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.stereotype.Component; @Component public class BaseJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap data = context.getJobDetail().getJobDataMap(); QuartzJob job = (QuartzJob) data.get("job"); try { TaskUtils.executeJob(job); } catch (Exception e) { throw new JobExecutionException(e); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/JobExecuter.java ================================================ package cn.enilu.material.service.task; import cn.enilu.material.bean.entity.system.Task; import cn.enilu.material.bean.entity.system.TaskLog; import cn.enilu.material.bean.vo.QuartzJob; import cn.enilu.material.dao.system.TaskLogRepository; import cn.enilu.material.utils.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Date; import java.util.Map; @Component public abstract class JobExecuter { protected static final Logger log = LoggerFactory.getLogger(JobExecuter.class); @Autowired private TaskService taskService; @Autowired private TaskLogRepository taskLogRepository; private QuartzJob job; public void setJob(QuartzJob job) { this.job = job; } public void execute() { Map dataMap = job.getDataMap(); String taskId = job.getJobName(); Task task = taskService.get(Long.valueOf(taskId)); final String taskName = task.getName(); log.info(">>>>>>>>>>>>>>>>>开始执行定时任务[" + taskName + "]...<<<<<<<<<<<<<<<<<<<"); String exeResult = "执行成功"; final TaskLog taskLog = new TaskLog(); taskLog.setName(taskName); final Date exeAt = new Date(); taskLog.setExecAt(exeAt); taskLog.setIdTask(task.getId()); //默认是成功 出异常后改成失败 taskLog.setExecSuccess(TaskLog.EXE_SUCCESS_RESULT); try { execute(dataMap); } catch (Exception e) { log.error("exeucte " + getClass().getName() + " error : ", e); exeResult = "执行失败\n"; exeResult += ExceptionUtils.getStackTrace(e); taskLog.setExecSuccess(TaskLog.EXE_FAILURE_RESULT); taskLog.setJobException(e.getClass().getName()); } task.setExecResult(exeResult); task.setExecAt(exeAt); taskLogRepository.save(taskLog); taskService.simpleUpdate(task); log.info(">>>>>>>>>>>>>>>>>执行定时任务[" + taskName + "]结束<<<<<<<<<<<<<<<<<<<"); } /** * * @param dataMap 数据库配置的参数 */ public abstract void execute(Map dataMap) throws Exception; public String getEmail() { return getEmail("snowalert@xuezhongdai.cn"); } public String getEmail(String defaultEmail) { Map dataMap = job.getDataMap(); String toEmail = null; if (dataMap != null && dataMap.containsKey("email")) { toEmail = StringUtils.sNull(dataMap.get("email")); } if (StringUtils.isEmpty(toEmail)) { toEmail = defaultEmail; } return toEmail; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/JobService.java ================================================ package cn.enilu.material.service.task; import cn.enilu.material.bean.entity.system.Task; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.exception.ExceptionEnum; import cn.enilu.material.bean.vo.QuartzJob; import cn.enilu.material.bean.vo.query.SearchFilter; import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 任务服务 */ @Service public class JobService { private static final Logger logger = LoggerFactory.getLogger(JobService.class); @Autowired private Scheduler scheduler; @Autowired private TaskService taskService; /** * 获取单个任务 * * @param jobName * @param jobGroup * @return * @throws SchedulerException */ public QuartzJob getJob(String jobName, String jobGroup) throws SchedulerException { QuartzJob job = null; TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); Trigger trigger = scheduler.getTrigger(triggerKey); if (null != trigger) { job = new QuartzJob(); job.setJobName(jobName); job.setJobGroup(jobGroup); job.setDescription("触发器:" + trigger.getKey()); job.setNextTime(trigger.getNextFireTime()); job.setPreviousTime(trigger.getPreviousFireTime()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } } return job; } public List getTaskList() { List tasks = taskService.queryAll(SearchFilter.build("disabled", SearchFilter.Operator.EQ,false)); List jobs = new ArrayList<>(); for (Task task : tasks) { jobs.add(getJob(task)); } return jobs; } public QuartzJob getJob(Task task) { QuartzJob job = null; if (task != null) { job = new QuartzJob(); job.setJobName(String.valueOf(task.getId())); job.setJobGroup(task.getJobGroup()); job.setCronExpression(task.getCron()); job.setConcurrent(task.isConcurrent()); job.setJobClass(task.getJobClass()); job.setDescription(task.getName()); job.setDisabled(task.isDisabled()); if (StringUtils.isNotBlank(task.getData())) { try { Map dataMap = JSON.parseObject( task.getData(),Map.class); job.setDataMap(dataMap); } catch (Exception e) { throw new ApplicationException(ExceptionEnum.TASK_CONFIG_ERROR); } } } return job; } /** * 添加任务 * * @param job * @throws SchedulerException */ public boolean addJob(QuartzJob job) throws SchedulerException { logger.info("新增任务Id:{}, name:{}", job.getJobName(), job.getDescription()); if (job == null || job.isDisabled()) { return false; } if (!TaskUtils.isValidExpression(job.getCronExpression())) { logger.error("时间表达式错误(" + job.getJobName() + "," + job.getJobGroup() + ")," + job.getCronExpression()); return false; } else { // 任务名称和任务组设置规则: // 名称:task_1 .. // 组 :group_1 .. TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 不存在,创建一个 if (null == trigger) { //是否允许并发执行 Class clazz = job.isConcurrent() ? BaseJob.class : NoConurrentBaseJob.class; JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build(); jobDetail.getJobDataMap().put("job", job); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); // 按新的表达式构建一个新的trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger); } else { // trigger已存在,则更新相应的定时设置 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); // 按新的cronExpression表达式重新构建trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(triggerKey, trigger); } } return true; } /** * 删除任务 */ public boolean deleteJob(QuartzJob job) { logger.info("删除任务:{}", job.getJobName()); JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup()); try { scheduler.deleteJob(jobKey); return true; } catch (SchedulerException e) { logger.error(e.getMessage(), e); } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/NoConurrentBaseJob.java ================================================ package cn.enilu.material.service.task; import org.quartz.DisallowConcurrentExecution; import org.springframework.stereotype.Component; @Component @DisallowConcurrentExecution public class NoConurrentBaseJob extends BaseJob { } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/QuartzConfigration.java ================================================ package cn.enilu.material.service.task; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; @Configuration public class QuartzConfigration { @Bean(name = "scheduler") public SchedulerFactoryBean schedulerFactory() { SchedulerFactoryBean bean = new SchedulerFactoryBean(); return bean; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/TaskService.java ================================================ package cn.enilu.material.service.task; import cn.enilu.material.bean.entity.system.Task; import cn.enilu.material.bean.entity.system.TaskLog; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.exception.ExceptionEnum; import cn.enilu.material.bean.vo.QuartzJob; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.dao.system.TaskLogRepository; import cn.enilu.material.dao.system.TaskRepository; import cn.enilu.material.service.BaseService; import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; /** * 任务计划服务 */ @Service public class TaskService extends BaseService { private static final Logger logger = LoggerFactory.getLogger(TaskService.class); @Autowired private TaskRepository taskRepository; @Autowired private TaskLogRepository taskLogRepository; @Autowired private JobService jobService; public Task save(Task task) { logger.info("新增定时任务%s", task.getName()); task = taskRepository.save(task); try { jobService.addJob(jobService.getJob(task)); } catch (SchedulerException e) { logger.error(e.getMessage(), e); } return task; } @Override public Task update(Task record) { logger.info("更新定时任务{}", record.getName()); taskRepository.save(record); try { QuartzJob job = jobService.getJob(record.getId().toString(), record.getJobGroup()); if (job != null) { jobService.deleteJob(job); } jobService.addJob(jobService.getJob(record)); } catch (SchedulerException e) { logger.error(e.getMessage(), e); } return record; } public boolean simpleUpdate(Task task) { taskRepository.save(task); return true; } public Task disable(Long id) { Task task = get(id); task.setDisabled(true); taskRepository.save(task); logger.info("禁用定时任务{}", id.toString()); try { QuartzJob job = jobService.getJob(task.getId().toString(), task.getJobGroup()); if (job != null) { jobService.deleteJob(job); } } catch (SchedulerException e) { logger.error(e.getMessage(), e); } return task; } public Task enable(Long id) { Task task = get(id); task.setDisabled(false); taskRepository.save(task); logger.info("启用定时任务{}", id.toString()); try { QuartzJob job = jobService.getJob(task.getId().toString(), task.getJobGroup()); if (job != null) { jobService.deleteJob(job); } if (!task.isDisabled()) { jobService.addJob(jobService.getJob(task)); } } catch (SchedulerException e) { throw new ApplicationException(ExceptionEnum.TASK_CONFIG_ERROR); } return task; } @Override public void delete(Long id) { Task task = get(id); task.setDisabled(true); taskRepository.delete(task); logger.info("删除定时任务{}", id.toString()); try { QuartzJob job = jobService.getJob(task); if (job != null) { jobService.deleteJob(job); } } catch (Exception e) { logger.error(e.getMessage(), e); } } public Page getTaskLogs(Page page, Long taskId) { Pageable pageable = null; if(page.isOpenSort()) { pageable = new PageRequest(page.getCurrent()-1, page.getSize(), page.isAsc() ? Sort.Direction.ASC : Sort.Direction.DESC, page.getOrderByField()); }else{ pageable = new PageRequest(page.getCurrent()-1,page.getSize(),Sort.Direction.DESC,"id"); } org.springframework.data.domain.Page taskLogPage = taskLogRepository.findAll(new Specification(){ @Override public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { List list = new ArrayList(); list.add(criteriaBuilder.equal(root.get("idTask").as(Long.class),taskId)); Predicate[] p = new Predicate[list.size()]; return criteriaBuilder.and(list.toArray(p)); } },pageable); page.setTotal(Integer.valueOf(taskLogPage.getTotalElements()+"")); page.setRecords(taskLogPage.getContent()); return page; } public Object findByNameLike(String name) { return taskRepository.findByNameLike(name); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/TaskUtils.java ================================================ package cn.enilu.material.service.task; import cn.enilu.material.bean.vo.QuartzJob; import cn.enilu.material.bean.vo.SpringContextHolder; import org.apache.commons.lang3.StringUtils; import org.quartz.impl.triggers.CronTriggerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.ParseException; import java.util.Date; /** * Created by czhou on 1/10/17. */ public class TaskUtils { private static final Logger logger = LoggerFactory.getLogger(TaskUtils.class); /** * 通过反射调用job中定义的方法 * * @param job * @throws Exception */ public static void executeJob(QuartzJob job) throws Exception { JobExecuter jobExecuter = null; Class clazz = null; if (StringUtils.isNotBlank(job.getJobClass())) { clazz = Class.forName(job.getJobClass()); jobExecuter = (JobExecuter) SpringContextHolder.getBean(clazz); jobExecuter.setJob(job); } if (jobExecuter == null) { throw new RuntimeException("任务名称 = [" + job.getDescription() + "]未启动成功,请检查执行类是否配置正确!!!"); } jobExecuter.execute(); } /** * 判断cron时间表达式正确性 * * @param cronExpression * @return */ public static boolean isValidExpression(final String cronExpression) { CronTriggerImpl trigger = new CronTriggerImpl(); try { trigger.setCronExpression(cronExpression); Date date = trigger.computeFirstFireTime(null); return date != null && date.after(new Date()); } catch (ParseException e) { logger.error(e.getMessage(), e); } return false; } public static void main(String[] args) { System.out.println(isValidExpression("0 0/1 * * * ?")); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/service/task/job/HelloJob.java ================================================ package cn.enilu.material.service.task.job; import cn.enilu.material.bean.entity.system.Cfg; import cn.enilu.material.service.system.CfgService; import cn.enilu.material.service.task.JobExecuter; import cn.enilu.material.utils.DateUtil; import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; /** * HelloJob * * @author zt * @version 2018/12/30 0030 */ @Component public class HelloJob extends JobExecuter { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private CfgService cfgService; @Override public void execute(Map dataMap) throws Exception { Cfg cfg = cfgService.get(1L); cfg.setCfgDesc("update by "+ DateUtil.getTime()); cfgService.update(cfg); logger.info("hello :"+JSON.toJSONString(dataMap)); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/ShiroDbRealm.java ================================================ package cn.enilu.material.shiro; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.shiro.factory.IShiro; import cn.enilu.material.shiro.factory.ShiroFactroy; import cn.enilu.material.utils.ToolUtil; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.HashSet; import java.util.List; import java.util.Set; public class ShiroDbRealm extends AuthorizingRealm { /** * 登录认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { IShiro shiroFactory = ShiroFactroy.me(); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = shiroFactory.user(token.getUsername()); ShiroUser shiroUser = shiroFactory.shiroUser(user); SimpleAuthenticationInfo info = shiroFactory.info(shiroUser, user, super.getName()); return info; } /** * 权限认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { IShiro shiroFactory = ShiroFactroy.me(); ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); List roleList = shiroUser.getRoleList(); Set permissionSet = new HashSet<>(); Set roleNameSet = new HashSet<>(); for (Long roleId : roleList) { List permissions = shiroFactory.findPermissionsByRoleId(roleId.intValue()); if (permissions != null) { for (String permission : permissions) { if (ToolUtil.isNotEmpty(permission)) { permissionSet.add(permission); } } } String roleName = shiroFactory.findRoleNameByRoleId(roleId); roleNameSet.add(roleName); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(permissionSet); info.addRoles(roleNameSet); return info; } /** * 设置认证加密方式 */ @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher(); md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName); md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations); super.setCredentialsMatcher(md5CredentialsMatcher); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/ShiroKit.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.shiro; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.ToolUtil; import org.apache.shiro.SecurityUtils; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import java.util.List; /** * shiro工具类 * * @author dafei, Chill Zhuang */ public class ShiroKit { private static final String NAMES_DELIMETER = ","; /** * 加盐参数 */ public final static String hashAlgorithmName = "MD5"; /** * 循环次数 */ public final static int hashIterations = 1024; /** * shiro密码加密工具类 * * @param credentials 密码 * @param saltSource 密码盐 * @return */ public static String md5(String credentials, String saltSource) { ByteSource salt = new Md5Hash(saltSource); return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations).toString(); } /** * 获取随机盐值 * @param length * @return */ public static String getRandomSalt(int length) { return ToolUtil.getRandomString(length); } /** * 获取当前 Subject * * @return Subject */ public static Subject getSubject() { return SecurityUtils.getSubject(); } /** * 获取封装的 ShiroUser * * @return ShiroUser */ public static ShiroUser getUser() { if (isGuest()) { return null; } else { return (ShiroUser) getSubject().getPrincipals().getPrimaryPrincipal(); } } /** * 从shiro获取session * */ public static Session getSession() { return getSubject().getSession(); } /** * 获取shiro指定的sessionKey * */ @SuppressWarnings("unchecked") public static T getSessionAttr(String key) { Session session = getSession(); return session != null ? (T) session.getAttribute(key) : null; } /** * 设置shiro指定的sessionKey * */ public static void setSessionAttr(String key, Object value) { Session session = getSession(); session.setAttribute(key, value); } /** * 移除shiro指定的sessionKey */ public static void removeSessionAttr(String key) { Session session = getSession(); if (session != null) { session.removeAttribute(key); } } /** * 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用 * * @param roleName * 角色名 * @return 属于该角色:true,否则false */ public static boolean hasRole(String roleName) { return getSubject() != null && roleName != null && roleName.length() > 0 && getSubject().hasRole(roleName); } /** * 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 * * @param roleName * 角色名 * @return 不属于该角色:true,否则false */ public static boolean lacksRole(String roleName) { return !hasRole(roleName); } /** * 验证当前用户是否属于以下任意一个角色。 * * @param roleNames * 角色列表 * @return 属于:true,否则false */ public static boolean hasAnyRoles(String roleNames) { boolean hasAnyRole = false; Subject subject = getSubject(); if (subject != null && roleNames != null && roleNames.length() > 0) { for (String role : roleNames.split(NAMES_DELIMETER)) { if (subject.hasRole(role.trim())) { hasAnyRole = true; break; } } } return hasAnyRole; } /** * 验证当前用户是否属于以下所有角色。 * * @param roleNames * 角色列表 * @return 属于:true,否则false */ public static boolean hasAllRoles(String roleNames) { boolean hasAllRole = true; Subject subject = getSubject(); if (subject != null && roleNames != null && roleNames.length() > 0) { for (String role : roleNames.split(NAMES_DELIMETER)) { if (!subject.hasRole(role.trim())) { hasAllRole = false; break; } } } return hasAllRole; } /** * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用 * * @param permission * 权限名 * @return 拥有权限:true,否则false */ public static boolean hasPermission(String permission) { return getSubject() != null && permission != null && permission.length() > 0 && getSubject().isPermitted(permission); } /** * 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 * * @param permission * 权限名 * @return 拥有权限:true,否则false */ public static boolean lacksPermission(String permission) { return !hasPermission(permission); } /** * 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用 * * @return 通过身份验证:true,否则false */ public static boolean isAuthenticated() { return getSubject() != null && getSubject().isAuthenticated(); } /** * 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。 * * @return 没有通过身份验证:true,否则false */ public static boolean notAuthenticated() { return !isAuthenticated(); } /** * 认证通过或已记住的用户。与guset搭配使用。 * * @return 用户:true,否则 false */ public static boolean isUser() { return getSubject() != null && getSubject().getPrincipal() != null; } /** * 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用 * * @return 访客:true,否则false */ public static boolean isGuest() { return !isUser(); } /** * 输出当前用户信息,通常为登录帐号信息。 * * @return 当前用户信息 */ public static String principal() { if (getSubject() != null) { Object principal = getSubject().getPrincipal(); return principal.toString(); } return ""; } /** * 获取当前用户的部门数据范围的集合 */ public static List getDeptDataScope() { Long deptId = getUser().getDeptId(); List subDeptIds = ConstantFactory.me().getSubDeptId(deptId); subDeptIds.add(deptId); return subDeptIds; } /** * 判断当前用户是否是超级管理员 */ public static boolean isAdmin() { List roleList = ShiroKit.getUser().getRoleList(); for (Long integer : roleList) { String singleRoleTip = ConstantFactory.me().getSingleRoleTip(integer); if (singleRoleTip.equals(Const.ADMIN_NAME)) { return true; } } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/check/ICheck.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.shiro.check; /** * 检查用接口 */ public interface ICheck { /** * 检查指定角色 * @param permissions * @return boolean */ boolean check(Object[] permissions); /** * 检查全体角色 * @return boolean */ boolean checkAll(); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/check/PermissionCheckFactory.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.shiro.check; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.CollectionKit; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.web.listener.ConfigListener; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; /** * 权限自定义检查 */ @Service @DependsOn("springContextHolder") @Transactional(readOnly = true) public class PermissionCheckFactory implements ICheck { public static ICheck me() { return SpringContextHolder.getBean(ICheck.class); } @Override public boolean check(Object[] permissions) { ShiroUser user = ShiroKit.getUser(); if (null == user) { return false; } String join = CollectionKit.join(permissions, ","); if (ShiroKit.hasAnyRoles(join)) { return true; } return false; } @Override public boolean checkAll() { HttpServletRequest request = HttpKit.getRequest(); ShiroUser user = ShiroKit.getUser(); if (null == user) { return false; } String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), ""); String[] str = requestURI.split("/"); if (str.length > 3) { requestURI = "/" + str[1] + "/" + str[2]; } if (ShiroKit.hasPermission(requestURI)) { return true; } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/check/PermissionCheckManager.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.shiro.check; import cn.enilu.material.bean.vo.SpringContextHolder; /** * 权限检查工厂 */ public class PermissionCheckManager { private final static PermissionCheckManager me = new PermissionCheckManager(); private ICheck defaultCheckFactory = SpringContextHolder.getBean(ICheck.class); public static PermissionCheckManager me() { return me; } private PermissionCheckManager() { } public PermissionCheckManager(ICheck checkFactory) { this.defaultCheckFactory = checkFactory; } public void setDefaultCheckFactory(ICheck defaultCheckFactory) { this.defaultCheckFactory = defaultCheckFactory; } public static boolean check(Object[] permissions) { return me.defaultCheckFactory.check(permissions); } public static boolean checkAll() { return me.defaultCheckFactory.checkAll(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/factory/IShiro.java ================================================ package cn.enilu.material.shiro.factory; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.entity.system.User; import org.apache.shiro.authc.SimpleAuthenticationInfo; import java.util.List; /** * 定义shirorealm所需数据的接口 * * @author fengshuonan * @date 2016年12月5日 上午10:23:34 */ public interface IShiro { /** * 根据账号获取登录用户 * * @param account 账号 */ User user(String account); /** * 根据系统用户获取Shiro的用户 * * @param user 系统用户 */ ShiroUser shiroUser(User user); /** * 获取权限列表通过角色id * * @param roleId 角色id */ List findPermissionsByRoleId(Integer roleId); /** * 根据角色id获取角色名称 * * @param roleId 角色id */ String findRoleNameByRoleId(Long roleId); /** * 获取shiro的认证信息 */ SimpleAuthenticationInfo info(ShiroUser shiroUser, User user, String realmName); } ================================================ FILE: material-core/src/main/java/cn/enilu/material/shiro/factory/ShiroFactroy.java ================================================ package cn.enilu.material.shiro.factory; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.bean.constant.state.ManagerStatus; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.bean.vo.node.MenuNode; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.dao.system.MenuRepository; import cn.enilu.material.dao.system.UserRepository; import cn.enilu.material.service.system.MenuService; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.Convert; import cn.enilu.material.utils.Lists; import org.apache.shiro.authc.CredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; import java.util.Map; @Service @DependsOn("springContextHolder") @Transactional(readOnly = true) public class ShiroFactroy implements IShiro { @Autowired private UserRepository userRepository; @Autowired private MenuRepository menuRepository; @Autowired private MenuService menuService; public static IShiro me() { return SpringContextHolder.getBean(IShiro.class); } @Override public User user(String account) { User user = userRepository.findByAccount(account); // 账号不存在 if (null == user) { throw new CredentialsException(); } // 账号被冻结 if (user.getStatus() != ManagerStatus.OK.getCode()) { throw new LockedAccountException(); } return user; } @Override public ShiroUser shiroUser(User user) { ShiroUser shiroUser = new ShiroUser(); shiroUser.setId(Long.valueOf(user.getId())); // 账号id shiroUser.setAccount(user.getAccount());// 账号 shiroUser.setDeptId(user.getDeptid()); // 部门id shiroUser.setDeptName(ConstantFactory.me().getDeptName(user.getDeptid()));// 部门名称 shiroUser.setName(user.getName()); // 用户名称 Long[] roleArray = Convert.toLongArray(",", user.getRoleid()); List roleList = new ArrayList(); List roleNameList = new ArrayList(); for (Long roleId : roleArray) { roleList.add(roleId); roleNameList.add(ConstantFactory.me().getSingleRoleName(roleId)); } shiroUser.setRoleList(roleList); shiroUser.setRoleNames(roleNameList); List menuNodes = menuService.getMenusByRoleIds(roleList); List titles = MenuNode.buildTitle(menuNodes); shiroUser.setTitles(titles); if(user.getAvatar()==null){ user.setAvatar("avatar.png"); } shiroUser.setProfile(user); return shiroUser; } @Override public List findPermissionsByRoleId(Integer roleId) { String sql = "select url from t_sys_relation rel inner join t_sys_menu m on rel.menuid = m.id where m.status=1 and rel.roleid=:roleId"; List list = menuService.queryBySql(sql, SearchFilter.build("roleId",roleId)); List resUrls = Lists.newArrayList(); if(list!=null&&!list.isEmpty()){ for(Map map :list){ resUrls.add(map.get("url").toString()); } } return resUrls; } @Override public String findRoleNameByRoleId(Long roleId) { return ConstantFactory.me().getSingleRoleTip(roleId); } @Override public SimpleAuthenticationInfo info(ShiroUser shiroUser, User user, String realmName) { String credentials = user.getPassword(); // 密码加盐处理 String source = user.getSalt(); ByteSource credentialsSalt = new Md5Hash(source); return new SimpleAuthenticationInfo(shiroUser, credentials, credentialsSalt, realmName); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/BasicType.java ================================================ package cn.enilu.material.utils; import java.util.HashMap; import java.util.Map; /** * 基本变量类型的枚举 * @author xiaoleilu */ public enum BasicType { BYTE, SHORT, INT, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, CHAR, CHARACTER, STRING; /** 原始类型为Key,包装类型为Value,例如: int.class -> Integer.class. */ public static final Map, Class> wrapperPrimitiveMap = new HashMap, Class>(8); /** 包装类型为Key,原始类型为Value,例如: Integer.class -> int.class. */ public static final Map, Class> primitiveWrapperMap = new HashMap, Class>(8); static { wrapperPrimitiveMap.put(Boolean.class, boolean.class); wrapperPrimitiveMap.put(Byte.class, byte.class); wrapperPrimitiveMap.put(Character.class, char.class); wrapperPrimitiveMap.put(Double.class, double.class); wrapperPrimitiveMap.put(Float.class, float.class); wrapperPrimitiveMap.put(Integer.class, int.class); wrapperPrimitiveMap.put(Long.class, long.class); wrapperPrimitiveMap.put(Short.class, short.class); for (Map.Entry, Class> entry : wrapperPrimitiveMap.entrySet()) { primitiveWrapperMap.put(entry.getValue(), entry.getKey()); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/BeanUtil.java ================================================ package cn.enilu.material.utils; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.springframework.beans.BeanUtils; import org.springframework.cglib.beans.BeanMap; import javax.persistence.Column; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Date; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created on 2018/2/26 0026. * * @author enilu */ public class BeanUtil { /** * 缓存字段名和中文注释对应关系的map */ private static Map fieldMap = Maps.newHashMap(); public static final Pattern COLUMN_DEFINITION_PATTERN = Pattern .compile("([A-Za-z]+)(?:\\(\\d+\\))?\\s*(?:(?:COMMENT|[Cc]omment)\\s+'(.*?)')?"); /** * 将对象装换为map * * @param bean * @return */ public static Map beanToMap(T bean) { Map map = Maps.newHashMap(); if (bean != null) { BeanMap beanMap = BeanMap.create(bean); for (Object key : beanMap.keySet()) { map.put(key + "", beanMap.get(key)); } } return map; } /** * 将map装换为javabean对象 * * @param map * @param bean * @return */ public static T mapToBean(Map map, T bean) { BeanMap beanMap = BeanMap.create(bean); beanMap.putAll(map); return bean; } /** * 将List转换为List> * * @param objList * @return */ public static List> objectsToMaps(List objList) { List> list = com.google.common.collect.Lists.newArrayList(); if (objList != null && objList.size() > 0) { Map map = null; T bean = null; for (int i = 0, size = objList.size(); i < size; i++) { bean = objList.get(i); map = beanToMap(bean); list.add(map); } } return list; } /** * 将List>转换为List * * @param maps * @param clazz * @return * @throws InstantiationException * @throws IllegalAccessException */ public static List mapsToObjects(List> maps, Class clazz) throws InstantiationException, IllegalAccessException { List list = com.google.common.collect.Lists.newArrayList(); if (maps != null && maps.size() > 0) { Map map = null; T bean = null; for (int i = 0, size = maps.size(); i < size; i++) { map = maps.get(i); bean = clazz.newInstance(); mapToBean(map, bean); list.add(bean); } } return list; } public static List objectToObjects(List objectList, Class clazz) throws InstantiationException, IllegalAccessException { List list = Lists.newArrayList(); if (objectList != null && objectList.size() > 0) { Object source = null; T bean = null; for (int i = 0, size = objectList.size(); i < size; i++) { source = objectList.get(i); bean = clazz.newInstance(); BeanUtils.copyProperties(source, bean); list.add(bean); } } return list; } /** * 比较两个对象pojo1和pojo2,并输出不一致信息 * * @param key * @param pojo1 * @param pojo2 * @return * @throws IllegalAccessException * @throws InstantiationException */ public static String contrastObj(String key, Object pojo1, Map pojo2) throws IllegalAccessException, InstantiationException { StringBuilder str = new StringBuilder(); String headerName = key; String headerValue = pojo2.get(key); try { Class clazz = pojo1.getClass(); Field[] fields = pojo1.getClass().getDeclaredFields(); int i = 1; for (Field field : fields) { if ("serialVersionUID".equals(field.getName())) { continue; } // PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); Method getMethod = getReadMethod(field.getName(), clazz); Object o1 = "null"; if (StringUtils.isNotNullOrEmpty(pojo2.get("id"))) { o1 = getMethod.invoke(pojo1); } Object o2 = pojo2.get(StringUtils.firstCharToLowerCase(getMethod.getName().substring(3))); if (StringUtils.equals(key, field.getName())) { headerName = getFieldComment(clazz, field); } if (o1 == null || o2 == null) { continue; } if (o1 instanceof Date) { o1 = DateUtil.getDay((Date) o1); } else if (o1 instanceof Integer) { o2 = Integer.parseInt(o2.toString()); } if (!o1.toString().equals(o2.toString())) { if (i != 1) { str.append(Constants.SEPARATOR); } String fieldName = getFieldComment(clazz, field); str.append(fieldName + ":" + o1 + "=>" + o2); i++; } } } catch (Exception e) { } String header = headerName + "=" + headerValue + Constants.SEPARATOR; return header + str; } private static Method getReadMethod(String fieldName, Class clazz) { String methodName = "get" + StringUtils.firstCharToUpperCase(fieldName); Method method = null; ; try { method = clazz.getDeclaredMethod(methodName); } catch (Exception e) { methodName = "is" + StringUtils.firstCharToUpperCase(fieldName); try { method = clazz.getDeclaredMethod(methodName); } catch (Exception e1) { e1.printStackTrace(); } } return method; } /** * 从实体类的columDefinition中获取字段的中文注释,如果 * * @param clazz * @param field * @return */ public static String getFieldComment(Class clazz, Field field) { String key = clazz.getName() + field.getName(); String comment = fieldMap.get(key); if (comment == null) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof Column) { Column columnAnno = (Column) annotation; String columnDefinition = columnAnno.columnDefinition(); if (columnDefinition != null && !"".equals(columnDefinition.trim())) { Matcher matcher = COLUMN_DEFINITION_PATTERN.matcher(columnDefinition.trim()); if (matcher.find()) { comment = matcher.group(2); fieldMap.put(key, comment); break; } } } } } if (comment == null) { comment = field.getName(); fieldMap.put(key, comment); } return comment; } /** * 解析多个key(逗号隔开的) */ public static String parseMutiKey(Map requests) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : requests.entrySet()) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";"); } return sb.toString(); } /** * 对象组中是否存在 Empty Object * * @param os 对象组 * @return */ public static boolean isOneEmpty(Object... os) { for (Object o : os) { if (StringUtils.isNullOrEmpty(o)) { return true; } } return false; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/BirthUtils.java ================================================ package cn.enilu.material.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; public class BirthUtils { public static final String[] zodiacArr = { "猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊" }; public static final String[] constellationArr = { "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "魔羯座" }; public static final int[] constellationEdgeDay = { 20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22 }; /** * 根据日期获取生肖 * @return */ public static String getZodica(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return zodiacArr[cal.get(Calendar.YEAR) % 12]; } /** * 根据日期获取星座 * @return */ public static String getConstellation(Date date) { if (date == null) { return ""; } Calendar cal = Calendar.getInstance(); cal.setTime(date); int month = cal.get(Calendar.MONTH); int day = cal.get(Calendar.DAY_OF_MONTH); if (day < constellationEdgeDay[month]) { month = month - 1; } if (month >= 0) { return constellationArr[month]; } // default to return 魔羯 return constellationArr[11]; } /** * 根据身份证号判断用户性别 * @param cardNo * @return */ public static String getSex(String cardNo) { String sexStr = "0"; if (cardNo.length() == 15) { sexStr = cardNo.substring(14, 15); } else if (cardNo.length() == 18) { sexStr = cardNo.substring(16,17); } int sexNo = Integer.parseInt(sexStr); return sexNo % 2 == 0 ? "女" : "男"; } /** * 根据身份证号判断用户生肖 * @param cardNo * @return */ public static String getZodica(String cardNo) { // 获取出生日期 String birthday = cardNo.substring(6, 14); Date birthdate = null; try { birthdate = new SimpleDateFormat("yyyyMMdd").parse(birthday); return getZodica(birthdate); } catch (ParseException e) { e.printStackTrace(); } return null; } /** * 根据身份证号判断用户星座 * @param cardNo * @return */ public static String getConstellation(String cardNo) { // 获取出生日期 String birthday = cardNo.substring(6, 14); Date birthdate = null; try { birthdate = new SimpleDateFormat("yyyyMMdd").parse(birthday); return getConstellation(birthdate); } catch (ParseException e) { e.printStackTrace(); } return null; } public static int getAge(String cardNo) { String birthday = cardNo.substring(6, 14); Date birthdate = null; try { birthdate = new SimpleDateFormat("yyyyMMdd").parse(birthday); } catch (ParseException e) { e.printStackTrace(); } GregorianCalendar currentDay = new GregorianCalendar(); currentDay.setTime(birthdate); int birYear = currentDay.get(Calendar.YEAR); // 获取年龄 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy"); String thisYear = simpleDateFormat.format(new Date()); int age = Integer.parseInt(thisYear) - birYear; return age; } public static int getAge(String cardNo, Date date) { String birthday = cardNo.substring(6, 14); Date birthdate = null; try { birthdate = new SimpleDateFormat("yyyyMMdd").parse(birthday); } catch (ParseException e) { e.printStackTrace(); } GregorianCalendar currentDay = new GregorianCalendar(); currentDay.setTime(birthdate); int birYear = currentDay.get(Calendar.YEAR); // 获取年龄 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy"); String thisYear = simpleDateFormat.format(date); int age = Integer.parseInt(thisYear) - birYear; return age; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/CollectionKit.java ================================================ package cn.enilu.material.utils; import cn.enilu.material.utils.cache.exception.ToolBoxException; import java.lang.reflect.Array; import java.util.*; import java.util.Map.Entry; /** * 集合相关工具类,包括数组 * * @author xiaoleilu * */ public class CollectionKit { private CollectionKit() { // 静态类不可实例化 } /** * 以 conjunction 为分隔符将集合转换为字符串 * * @param 被处理的集合 * @param collection 集合 * @param conjunction 分隔符 * @return 连接后的字符串 */ public static String join(Iterable collection, String conjunction) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (T item : collection) { if (isFirst) { isFirst = false; } else { sb.append(conjunction); } sb.append(item); } return sb.toString(); } /** * 以 conjunction 为分隔符将数组转换为字符串 * * @param 被处理的集合 * @param array 数组 * @param conjunction 分隔符 * @return 连接后的字符串 */ public static String join(T[] array, String conjunction) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (T item : array) { if (isFirst) { isFirst = false; } else { sb.append(conjunction); } sb.append(item); } return sb.toString(); } /** * 将多个集合排序并显示不同的段落(分页) * @param pageNo 页码 * @param numPerPage 每页的条目数 * @param comparator 比较器 * @param colls 集合数组 * @return 分页后的段落内容 */ @SafeVarargs public static List sortPageAll(int pageNo, int numPerPage, Comparator comparator, Collection... colls) { final List result = new ArrayList(); for (Collection coll : colls) { result.addAll(coll); } Collections.sort(result, comparator); //第一页且数目少于第一页显示的数目 if(pageNo <=1 && result.size() <= numPerPage) { return result; } final int[] startEnd = PageKit.transToStartEnd(pageNo, numPerPage); return result.subList(startEnd[0], startEnd[1]); } /** * 将多个集合排序并显示不同的段落(分页) * @param pageNo 页码 * @param numPerPage 每页的条目数 * @param comparator 比较器 * @param colls 集合数组 * @return 分业后的段落内容 */ // @SafeVarargs // public static List sortPageAll2(int pageNo, int numPerPage, Comparator comparator, Collection... colls) { // BoundedPriorityQueue queue = new BoundedPriorityQueue(pageNo * numPerPage); // for (Collection coll : colls) { // queue.addAll(coll); // } // // //第一页且数目少于第一页显示的数目 // if(pageNo <=1 && queue.size() <= numPerPage) { // return queue.toList(); // } // // final int[] startEnd = PageKit.transToStartEnd(pageNo, numPerPage); // return queue.toList().subList(startEnd[0], startEnd[1]); // } /** * 将Set排序(根据Entry的值) * * @param set 被排序的Set * @return 排序后的Set */ public static List> sortEntrySetToList(Set> set) { List> list = new LinkedList>(set); Collections.sort(list, new Comparator>(){ @Override public int compare(Entry o1, Entry o2) { if (o1.getValue() > o2.getValue()){ return 1; } if (o1.getValue() < o2.getValue()){ return -1; } return 0; } }); return list; } /** * 切取部分数据 * * @param 集合元素类型 * @param surplusAlaDatas 原数据 * @param partSize 每部分数据的长度 * @return 切取出的数据或null */ public static List popPart(Stack surplusAlaDatas, int partSize) { if (surplusAlaDatas == null || surplusAlaDatas.size() <= 0){ return null; } final List currentAlaDatas = new ArrayList(); int size = surplusAlaDatas.size(); // 切割 if (size > partSize) { for (int i = 0; i < partSize; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } else { for (int i = 0; i < size; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } return currentAlaDatas; } /** * 切取部分数据 * * @param 集合元素类型 * @param surplusAlaDatas 原数据 * @param partSize 每部分数据的长度 * @return 切取出的数据或null */ public static List popPart(Deque surplusAlaDatas, int partSize) { if (surplusAlaDatas == null || surplusAlaDatas.size() <= 0){ return null; } final List currentAlaDatas = new ArrayList(); int size = surplusAlaDatas.size(); // 切割 if (size > partSize) { for (int i = 0; i < partSize; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } else { for (int i = 0; i < size; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } return currentAlaDatas; } /** * 新建一个HashMap * * @return HashMap对象 */ public static HashMap newHashMap() { return new HashMap(); } /** * 新建一个HashMap * @param size 初始大小,由于默认负载因子0.75,传入的size会实际初始大小为size / 0.75 * @return HashMap对象 */ public static HashMap newHashMap(int size) { return new HashMap((int)(size / 0.75)); } /** * 新建一个HashSet * * @return HashSet对象 */ public static HashSet newHashSet() { return new HashSet(); } /** * 新建一个HashSet * * @return HashSet对象 */ @SafeVarargs public static HashSet newHashSet(T... ts) { HashSet set = new HashSet(); for (T t : ts) { set.add(t); } return set; } /** * 新建一个ArrayList * * @return ArrayList对象 */ public static ArrayList newArrayList() { return new ArrayList(); } /** * 新建一个ArrayList * * @return ArrayList对象 */ @SafeVarargs public static ArrayList newArrayList(T... values) { return new ArrayList(Arrays.asList(values)); } /** * 将新元素添加到已有数组中
* 添加新元素会生成一个新的数组,不影响原数组 * * @param buffer 已有数组 * @param newElement 新元素 * @return 新数组 */ public static T[] append(T[] buffer, T newElement) { T[] t = resize(buffer, buffer.length + 1, newElement.getClass()); t[buffer.length] = newElement; return t; } /** * 生成一个新的重新设置大小的数组 * * @param buffer 原数组 * @param newSize 新的数组大小 * @param componentType 数组元素类型 * @return 调整后的新数组 */ public static T[] resize(T[] buffer, int newSize, Class componentType) { T[] newArray = newArray(componentType, newSize); System.arraycopy(buffer, 0, newArray, 0, buffer.length >= newSize ? newSize : buffer.length); return newArray; } /** * 新建一个空数组 * @param componentType 元素类型 * @param newSize 大小 * @return 空数组 */ @SuppressWarnings("unchecked") public static T[] newArray(Class componentType, int newSize) { return (T[]) Array.newInstance(componentType, newSize); } /** * 生成一个新的重新设置大小的数组
* 新数组的类型为原数组的类型 * * @param buffer 原数组 * @param newSize 新的数组大小 * @return 调整后的新数组 */ public static T[] resize(T[] buffer, int newSize) { return resize(buffer, newSize, buffer.getClass().getComponentType()); } /** * 将多个数组合并在一起
* 忽略null的数组 * * @param arrays 数组集合 * @return 合并后的数组 */ @SafeVarargs public static T[] addAll(T[]... arrays) { if (arrays.length == 1) { return arrays[0]; } int length = 0; for (T[] array : arrays) { if(array == null) { continue; } length += array.length; } T[] result = newArray(arrays.getClass().getComponentType().getComponentType(), length); length = 0; for (T[] array : arrays) { if(array == null) { continue; } System.arraycopy(array, 0, result, length, array.length); length += array.length; } return result; } /** * 克隆数组 * @param array 被克隆的数组 * @return 新数组 */ public static T[] clone(T[] array) { if (array == null) { return null; } return array.clone(); } /** * 生成一个数字列表
* 自动判定正序反序 * @param excludedEnd 结束的数字(不包含) * @return 数字列表 */ public static int[] range(int excludedEnd) { return range(0, excludedEnd, 1); } /** * 生成一个数字列表
* 自动判定正序反序 * @param includedStart 开始的数字(包含) * @param excludedEnd 结束的数字(不包含) * @return 数字列表 */ public static int[] range(int includedStart, int excludedEnd) { return range(includedStart, excludedEnd, 1); } /** * 生成一个数字列表
* 自动判定正序反序 * @param includedStart 开始的数字(包含) * @param excludedEnd 结束的数字(不包含) * @param step 步进 * @return 数字列表 */ public static int[] range(int includedStart, int excludedEnd, int step) { if(includedStart > excludedEnd) { int tmp = includedStart; includedStart = excludedEnd; excludedEnd = tmp; } if(step <=0) { step = 1; } int deviation = excludedEnd - includedStart; int length = deviation / step; if(deviation % step != 0) { length += 1; } int[] range = new int[length]; for(int i = 0; i < length; i++) { range[i] = includedStart; includedStart += step; } return range; } /** * 截取数组的部分 * @param list 被截取的数组 * @param start 开始位置(包含) * @param end 结束位置(不包含) * @return 截取后的数组,当开始位置超过最大时,返回null */ public static List sub(List list, int start, int end) { if(list == null || list.isEmpty()) { return null; } if(start < 0) { start = 0; } if(end < 0) { end = 0; } if(start > end) { int tmp = start; start = end; end = tmp; } final int size = list.size(); if(end > size) { if(start >= size) { return null; } end = size; } return list.subList(start, end); } /** * 截取集合的部分 * @param list 被截取的数组 * @param start 开始位置(包含) * @param end 结束位置(不包含) * @return 截取后的数组,当开始位置超过最大时,返回null */ public static List sub(Collection list, int start, int end) { if(list == null || list.isEmpty()) { return null; } return sub(new ArrayList(list), start, end); } /** * 数组是否为空 * @param array 数组 * @return 是否为空 */ public static boolean isEmpty(T[] array) { return array == null || array.length == 0; } /** * 数组是否为非空 * @param array 数组 * @return 是否为非空 */ public static boolean isNotEmpty(T[] array) { return false == isEmpty(array); } /** * 集合是否为空 * @param collection 集合 * @return 是否为空 */ public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /** * 集合是否为非空 * @param collection 集合 * @return 是否为非空 */ public static boolean isNotEmpty(Collection collection) { return false == isEmpty(collection); } /** * Map是否为空 * @param map 集合 * @return 是否为空 */ public static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } /** * Map是否为非空 * @param map 集合 * @return 是否为非空 */ public static boolean isNotEmpty(Map map) { return false == isEmpty(map); } /** * 映射键值(参考Python的zip()函数)
* 例如:
* keys = [a,b,c,d]
* values = [1,2,3,4]
* 则得到的Map是 {a=1, b=2, c=3, d=4}
* 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(T[] keys, K[] values) { if(isEmpty(keys) || isEmpty(values)) { return null; } final int size = Math.min(keys.length, values.length); final Map map = new HashMap((int)(size / 0.75)); for(int i = 0; i < size; i++) { map.put(keys[i], values[i]); } return map; } /** * 映射键值(参考Python的zip()函数)
* 例如:
* keys = a,b,c,d
* values = 1,2,3,4
* delimiter = , * 则得到的Map是 {a=1, b=2, c=3, d=4}
* 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(String keys, String values, String delimiter) { return zip(StrKit.split(keys, delimiter), StrKit.split(values, delimiter)); } /** * 映射键值(参考Python的zip()函数)
* 例如:
* keys = [a,b,c,d]
* values = [1,2,3,4]
* 则得到的Map是 {a=1, b=2, c=3, d=4}
* 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(Collection keys, Collection values) { if(isEmpty(keys) || isEmpty(values)) { return null; } final List keyList = new ArrayList(keys); final List valueList = new ArrayList(values); final int size = Math.min(keys.size(), values.size()); final Map map = new HashMap((int)(size / 0.75)); for(int i = 0; i < size; i++) { map.put(keyList.get(i), valueList.get(i)); } return map; } /** * 数组中是否包含元素 * @param array 数组 * @param value 被检查的元素 * @return 是否包含 */ public static boolean contains(T[] array, T value) { final Class componetType = array.getClass().getComponentType(); boolean isPrimitive = false; if(null != componetType) { isPrimitive = componetType.isPrimitive(); } for (T t : array) { if(t == value) { return true; }else if(false == isPrimitive && null != value && value.equals(t)) { return true; } } return false; } /** * 将Entry集合转换为HashMap * @param entryCollection entry集合 * @return Map */ public static HashMap toMap(Collection> entryCollection) { HashMap map = new HashMap(); for (Entry entry : entryCollection) { map.put(entry.getKey(), entry.getValue()); } return map; } /** * 将集合转换为排序后的TreeSet * @param collection 集合 * @param comparator 比较器 * @return treeSet */ public static TreeSet toTreeSet(Collection collection, Comparator comparator){ final TreeSet treeSet = new TreeSet(comparator); for (T t : collection) { treeSet.add(t); } return treeSet; } /** * 排序集合 * @param collection 集合 * @param comparator 比较器 * @return treeSet */ public static List sort(Collection collection, Comparator comparator){ List list = new ArrayList(collection); Collections.sort(list, comparator); return list; } //------------------------------------------------------------------- 基本类型的数组转换为包装类型数组 /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Integer[] wrap(int... values){ final int length = values.length; Integer[] array = new Integer[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Long[] wrap(long... values){ final int length = values.length; Long[] array = new Long[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Character[] wrap(char... values){ final int length = values.length; Character[] array = new Character[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Byte[] wrap(byte... values){ final int length = values.length; Byte[] array = new Byte[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Short[] wrap(short... values){ final int length = values.length; Short[] array = new Short[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Float[] wrap(float... values){ final int length = values.length; Float[] array = new Float[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Double[] wrap(double... values){ final int length = values.length; Double[] array = new Double[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Boolean[] wrap(boolean... values){ final int length = values.length; Boolean[] array = new Boolean[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 判定给定对象是否为数组类型 * @param obj 对象 * @return 是否为数组类型 */ public static boolean isArray(Object obj){ return obj.getClass().isArray(); } /** * 数组或集合转String * * @param obj 集合或数组对象 * @return 数组字符串,与集合转字符串格式相同 */ public static String toString(Object obj) { if (null == obj) { return null; } if (isArray(obj)) { try { return Arrays.deepToString((Object[]) obj); } catch (Exception e) { final String className = obj.getClass().getComponentType().getName(); switch (className) { case "long": return Arrays.toString((long[]) obj); case "int": return Arrays.toString((int[]) obj); case "short": return Arrays.toString((short[]) obj); case "char": return Arrays.toString((char[]) obj); case "byte": return Arrays.toString((byte[]) obj); case "boolean": return Arrays.toString((boolean[]) obj); case "float": return Arrays.toString((float[]) obj); case "double": return Arrays.toString((double[]) obj); default: throw new ToolBoxException(e); } } } return obj.toString(); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/Constants.java ================================================ package cn.enilu.material.utils; public interface Constants { String SEPARATOR = ";;;"; long SYSTEM_USER_ID=-1; /** * 用户密码加密key */ String CRYPT_DES_KEY = "sc123456"; /** * 微信获取token url */ String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; /** * 微信获取JsapiTicket url */ String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"; /** * 微信下载多媒体文件 url */ String MEDIA_URL = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s"; /** * 微信群发信息 url */ String WX_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=%s"; /** * ===================================================================JSON * 返回JSON结果的前后缀,jquery1.4x以上版本的json校验比较严格,不能使用单引号,但是可以将jquery的json解析器还原为eval * ("("+xx+")") */ String JSON_PRE = "{'state':'", JSON_END = "'}"; /** * 定义JSON形式的业务处理结果 */ String FAIL = JSON_PRE + "fail" + JSON_END, ERROR = JSON_PRE + "error" + JSON_END, SUCCESS = JSON_PRE + "success" + JSON_END, TRUE = JSON_PRE + "true" + JSON_END, FALSE = JSON_PRE + "false" + JSON_END, LOCKED = JSON_PRE + "locked" + JSON_END, EXIST = JSON_PRE + "exist" + JSON_END; /** * 定义JSON形式的业务处理结果----附加上信息 */ static String msg(String state, Object info) { return state.replace("}", ", 'info': '" + info + "'}"); } /** * ===================================================================日志类型 * 访问日志、操作日志 */ int ACCESS = 0, OPERATION = 1; String MAIL_ACTION_KEY = "fqufyasiduvqeouirtfu"; } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/Convert.java ================================================ package cn.enilu.material.utils; import cn.enilu.material.utils.cache.exception.ToolBoxException; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; import java.text.NumberFormat; import java.util.Date; import java.util.Set; /** * 类型转换器 * * @author xiaoleilu * */ public class Convert { private Convert() { // 静态类不可实例化 } /** * 强制转换类型 * * @param clazz 被转换成的类型 * @param value 需要转换的对象 * @return 转换后的对象 */ public static Object parse(Class clazz, Object value) { try { if (clazz.isAssignableFrom(String.class)) { // ----2016-12-19---zhuangqian----防止beetlsql对空字符串不检测导致无法入库的问题---- if (StrKit.isBlank(String.valueOf(value))) return " "; else return String.valueOf(value); } return clazz.cast(value); } catch (ClassCastException e) { String valueStr = String.valueOf(value); Object result = parseBasic(clazz, valueStr); if (result != null) { return result; } if (Date.class.isAssignableFrom(clazz)) { // 判断标准日期 // ----2016-11-24---zhuangqian----需要加toDate(),不然beetlsql转换date类型的时候会报错---- return DateTimeKit.parse(valueStr).toDate(); } else if (clazz == BigInteger.class) { // 数学计算数字 return new BigInteger(valueStr); } else if (clazz == BigDecimal.class) { // 数学计算数字 return new BigDecimal(valueStr); } else if (clazz == byte[].class) { // 流,由于有字符编码问题,在此使用系统默认 return valueStr.getBytes(); } // 未找到可转换的类型,返回原值 return (StrKit.isBlank(valueStr)) ? null : value; } } /** * 转换基本类型
* 将字符串转换为原始类型或包装类型 * * @param clazz 转换到的类,可以是原始类型类,也可以是包装类型类 * @param valueStr 被转换的字符串 * @return 转换后的对象,如果非基本类型,返回null */ public static Object parseBasic(Class clazz, String valueStr) { if (null == clazz || null == valueStr) { return null; } if (StrKit.isBlank(valueStr)) return null; BasicType basicType = null; try { basicType = BasicType.valueOf(clazz.getSimpleName().toUpperCase()); } catch (Exception e) { // 非基本类型数据 return null; } switch (basicType) { case BYTE: if (clazz == byte.class) { return Byte.parseByte(valueStr); } return Byte.valueOf(valueStr); case SHORT: if (clazz == short.class) { return Short.parseShort(valueStr); } return Short.valueOf(valueStr); case INT: return Integer.parseInt(valueStr); case INTEGER: return Integer.valueOf(valueStr); case LONG: if (clazz == long.class) { return new BigDecimal(valueStr).longValue(); } return Long.valueOf(valueStr); case DOUBLE: if (clazz == double.class) { return new BigDecimal(valueStr).doubleValue(); } case FLOAT: if (clazz == float.class) { return Float.parseFloat(valueStr); } return Float.valueOf(valueStr); case BOOLEAN: if (clazz == boolean.class) { return Boolean.parseBoolean(valueStr); } return Boolean.valueOf(valueStr); case CHAR: return valueStr.charAt(0); case CHARACTER: return Character.valueOf(valueStr.charAt(0)); default: return null; } } /** * 转换为字符串
* 如果给定的值为null,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static String toStr(Object value, String defaultValue) { if (null == value) { return defaultValue; } if (value instanceof String) { return (String) value; } else if (CollectionKit.isArray(value)) { return CollectionKit.toString(value); } return value.toString(); } /** * 转换为字符串
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static String toStr(Object value) { return toStr(value, null); } /** * 转换为字符
* 如果给定的值为null,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Character toChar(Object value, Character defaultValue) { if (null == value) { return defaultValue; } if (value instanceof Character) { return (Character) value; } final String valueStr = toStr(value, null); return StrKit.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); } /** * 转换为字符
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Character toChar(Object value) { return toChar(value, null); } /** * 转换为byte
* 如果给定的值为null,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Byte toByte(Object value, Byte defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Byte) { return (Byte) value; } if (value instanceof Number) { return ((Number) value).byteValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return Byte.parseByte(valueStr); } catch (Exception e) { return defaultValue; } } /** * 转换为byte
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Byte toByte(Object value) { return toByte(value, null); } /** * 转换为Short
* 如果给定的值为null,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Short toShort(Object value, Short defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Short) { return (Short) value; } if (value instanceof Number) { return ((Number) value).shortValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return Short.parseShort(valueStr.trim()); } catch (Exception e) { return defaultValue; } } /** * 转换为Short
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Short toShort(Object value) { return toShort(value, null); } /** * 转换为Number
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Number toNumber(Object value, Number defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Number) { return (Number) value; } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return NumberFormat.getInstance().parse(valueStr); } catch (Exception e) { return defaultValue; } } /** * 转换为Number
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Number toNumber(Object value) { return toNumber(value, null); } /** * 转换为int
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Integer toInt(Object value, Integer defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Integer) { return (Integer) value; } if (value instanceof Number) { return ((Number) value).intValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return Integer.parseInt(valueStr.trim()); } catch (Exception e) { return defaultValue; } } /** * 转换为int
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Integer toInt(Object value) { return toInt(value, null); } /** * 转换为Integer数组
* * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null * @param values 被转换的值 * @return 结果 */ public static Integer[] toIntArray(boolean isIgnoreConvertError, Object... values) { if (CollectionKit.isEmpty(values)) { return new Integer[] {}; } final Integer[] ints = new Integer[values.length]; for (int i = 0; i < values.length; i++) { final Integer v = toInt(values[i], null); if (null == v && isIgnoreConvertError == false) { throw new ToolBoxException(StrKit.format("Convert [{}] to Integer error!", values[i])); } ints[i] = v; } return ints; } /** * 转换为Integer数组
* * @param str 被转换的值 * @return 结果 */ public static Integer[] toIntArray(String str) { return toIntArray(",", str); } /** * 转换为Integer数组
* * @param split 分隔符 * @param split 被转换的值 * @return 结果 */ public static Integer[] toIntArray(String split, String str) { if (StrKit.isEmpty(str)) { return new Integer[] {}; } String[] arr = str.split(split); final Integer[] ints = new Integer[arr.length]; for (int i = 0; i < arr.length; i++) { final Integer v = toInt(arr[i], 0); ints[i] = v; } return ints; } public static Long[] toLongArray(String split, String str) { if (StrKit.isEmpty(str)) { return new Long[] {}; } String[] arr = str.split(split); final Long[] ints = new Long[arr.length]; for (int i = 0; i < arr.length; i++) { final Long v = StringUtils.isNotEmpty(arr[i])?Long.valueOf(arr[i]):0L; ints[i] = v; } return ints; } /** * 转换为String数组
* * @param str 被转换的值 * @return 结果 */ public static String[] toStrArray(String str) { return toStrArray("", str); } /** * 转换为String数组
* * @param split 分隔符 * @param split 被转换的值 * @return 结果 */ public static String[] toStrArray(String split, String str) { return str.split(split); } /** * 转换为long
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Long toLong(Object value, Long defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Long) { return (Long) value; } if (value instanceof Number) { return ((Number) value).longValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { // 支持科学计数法 return new BigDecimal(valueStr.trim()).longValue(); } catch (Exception e) { return defaultValue; } } /** * 转换为long
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Long toLong(Object value) { return toLong(value, null); } /** * 转换为Long数组
* * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null * @param values 被转换的值 * @return 结果 */ public static Long[] toLongArray(boolean isIgnoreConvertError, Object... values) { if (CollectionKit.isEmpty(values)) { return new Long[] {}; } final Long[] longs = new Long[values.length]; for (int i = 0; i < values.length; i++) { final Long v = toLong(values[i], null); if (null == v && isIgnoreConvertError == false) { throw new ToolBoxException(StrKit.format("Convert [{}] to Long error!", values[i])); } longs[i] = v; } return longs; } /** * 转换为double
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Double toDouble(Object value, Double defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Double) { return (Double) value; } if (value instanceof Number) { return ((Number) value).doubleValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { // 支持科学计数法 return new BigDecimal(valueStr.trim()).doubleValue(); } catch (Exception e) { return defaultValue; } } /** * 转换为double
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Double toDouble(Object value) { return toDouble(value, null); } /** * 转换为Double数组
* * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null * @param values 被转换的值 * @return 结果 */ public static Double[] toDoubleArray(boolean isIgnoreConvertError, Object... values) { if (CollectionKit.isEmpty(values)) { return new Double[] {}; } final Double[] doubles = new Double[values.length]; for (int i = 0; i < values.length; i++) { final Double v = toDouble(values[i], null); if (null == v && isIgnoreConvertError == false) { throw new ToolBoxException(StrKit.format("Convert [{}] to Double error!", values[i])); } doubles[i] = v; } return doubles; } /** * 转换为Float
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Float toFloat(Object value, Float defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Float) { return (Float) value; } if (value instanceof Number) { return ((Number) value).floatValue(); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return Float.parseFloat(valueStr.trim()); } catch (Exception e) { return defaultValue; } } /** * 转换为Float
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Float toFloat(Object value) { return toFloat(value, null); } /** * 转换为Float数组
* * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null * @param values 被转换的值 * @return 结果 */ public static Float[] toFloatArray(boolean isIgnoreConvertError, Object... values) { if (CollectionKit.isEmpty(values)) { return new Float[] {}; } final Float[] floats = new Float[values.length]; for (int i = 0; i < values.length; i++) { final Float v = toFloat(values[i], null); if (null == v && isIgnoreConvertError == false) { throw new ToolBoxException(StrKit.format("Convert [{}] to Float error!", values[i])); } floats[i] = v; } return floats; } /** * 转换为boolean
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Boolean toBool(Object value, Boolean defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Boolean) { return (Boolean) value; } String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } valueStr = valueStr.trim().toLowerCase(); switch (valueStr) { case "true": return true; case "false": return false; case "yes": return true; case "ok": return true; case "no": return false; case "1": return true; case "0": return false; default: return defaultValue; } } /** * 转换为boolean
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Boolean toBool(Object value) { return toBool(value, null); } /** * 转换为Boolean数组
* * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null * @param values 被转换的值 * @return 结果 */ public static Boolean[] toBooleanArray(boolean isIgnoreConvertError, Object... values) { if (CollectionKit.isEmpty(values)) { return new Boolean[] {}; } final Boolean[] bools = new Boolean[values.length]; for (int i = 0; i < values.length; i++) { final Boolean v = toBool(values[i], null); if (null == v && isIgnoreConvertError == false) { throw new ToolBoxException(StrKit.format("Convert [{}] to Boolean error!", values[i])); } bools[i] = v; } return bools; } /** * 转换为Enum对象
* 如果给定的值为空,或者转换失败,返回默认值
* * @param clazz Enum的Class * @param value 值 * @param defaultValue 默认值 * @return Enum */ public static > E toEnum(Class clazz, Object value, E defaultValue) { if (value == null) { return defaultValue; } if (clazz.isAssignableFrom(value.getClass())) { @SuppressWarnings("unchecked") E myE = (E) value; return myE; } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return Enum.valueOf(clazz, valueStr); } catch (Exception e) { return defaultValue; } } /** * 转换为Enum对象
* 如果给定的值为空,或者转换失败,返回默认值null
* * @param clazz Enum的Class * @param value 值 * @return Enum */ public static > E toEnum(Class clazz, Object value) { return toEnum(clazz, value, null); } /** * 转换为BigInteger
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { if (value == null) { return defaultValue; } if (value instanceof BigInteger) { return (BigInteger) value; } if (value instanceof Long) { return BigInteger.valueOf((Long) value); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return new BigInteger(valueStr); } catch (Exception e) { return defaultValue; } } /** * 转换为BigInteger
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static BigInteger toBigInteger(Object value) { return toBigInteger(value, null); } /** * 转换为BigDecimal
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { if (value == null) { return defaultValue; } if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof Long) { return new BigDecimal((Long) value); } if (value instanceof Double) { return new BigDecimal((Double) value); } if (value instanceof Integer) { return new BigDecimal((Integer) value); } final String valueStr = toStr(value, null); if (StrKit.isBlank(valueStr)) { return defaultValue; } try { return new BigDecimal(valueStr); } catch (Exception e) { return defaultValue; } } /** * 转换为BigDecimal
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static BigDecimal toBigDecimal(Object value) { return toBigDecimal(value, null); } // ----------------------------------------------------------------------- 全角半角转换 /** * 半角转全角 * * @param input String. * @return 全角字符串. */ public static String toSBC(String input) { return toSBC(input, null); } /** * 半角转全角 * * @param input String * @param notConvertSet 不替换的字符集合 * @return 全角字符串. */ public static String toSBC(String input, Set notConvertSet) { char c[] = input.toCharArray(); for (int i = 0; i < c.length; i++) { if (null != notConvertSet && notConvertSet.contains(c[i])) { // 跳过不替换的字符 continue; } if (c[i] == ' ') { c[i] = '\u3000'; } else if (c[i] < '\177') { c[i] = (char) (c[i] + 65248); } } return new String(c); } /** * 全角转半角 * * @param input String. * @return 半角字符串 */ public static String toDBC(String input) { return toDBC(input, null); } /** * 替换全角为半角 * * @param text 文本 * @param notConvertSet 不替换的字符集合 * @return 替换后的字符 */ public static String toDBC(String text, Set notConvertSet) { char c[] = text.toCharArray(); for (int i = 0; i < c.length; i++) { if (null != notConvertSet && notConvertSet.contains(c[i])) { // 跳过不替换的字符 continue; } if (c[i] == '\u3000') { c[i] = ' '; } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { c[i] = (char) (c[i] - 65248); } } String returnString = new String(c); return returnString; } // --------------------------------------------------------------------- hex /** * 字符串转换成十六进制字符串 * * @param str 待转换的ASCII字符串 * @return 16进制字符串 */ public static String toHex(String str) { return HexKit.encodeHexStr(str.getBytes()); } /** * byte数组转16进制串 * * @param bytes 被转换的byte数组 * @return 转换后的值 */ public static String toHex(byte[] bytes) { return HexKit.encodeHexStr(bytes); } /** * Hex字符串转换为Byte值 * * @param src Byte字符串,每个Byte之间没有分隔符 * @return byte[] */ public static byte[] hexToBytes(String src) { return HexKit.decodeHex(src.toCharArray()); } /** * 十六进制转换字符串 * * @param hexStr Byte字符串(Byte之间无分隔符 如:[616C6B]) * @param charset 编码 {@link Charset} * @return 对应的字符串 */ public static String hexStrToStr(String hexStr, Charset charset) { return HexKit.decodeHexStr(hexStr, charset); } /** * String的字符串转换成unicode的String * * @param strText 全角字符串 * @return String 每个unicode之间无分隔符 * @throws Exception */ public static String strToUnicode(String strText) throws Exception { char c; StringBuilder str = new StringBuilder(); int intAsc; String strHex; for (int i = 0; i < strText.length(); i++) { c = strText.charAt(i); intAsc = (int) c; strHex = Integer.toHexString(intAsc); if (intAsc > 128) str.append("\\u" + strHex); else // 低位在前面补00 str.append("\\u00" + strHex); } return str.toString(); } /** * unicode的String转换成String的字符串 * * @param hex 16进制值字符串 (一个unicode为2byte) * @return String 全角字符串 */ public static String unicodeToStr(String hex) { int t = hex.length() / 6; StringBuilder str = new StringBuilder(); for (int i = 0; i < t; i++) { String s = hex.substring(i * 6, (i + 1) * 6); // 高位需要补上00再转 String s1 = s.substring(2, 4) + "00"; // 低位直接转 String s2 = s.substring(4); // 将16进制的string转为int int n = Integer.valueOf(s1, 16) + Integer.valueOf(s2, 16); // 将int转换为字符 char[] chars = Character.toChars(n); str.append(new String(chars)); } return str.toString(); } /** * 给定字符串转换字符编码
* 如果参数为空,则返回原字符串,不报错。 * * @param str 被转码的字符串 * @param sourceCharset 原字符集 * @param destCharset 目标字符集 * @return 转换后的字符串 */ public static String convertCharset(String str, String sourceCharset, String destCharset) { if (StrKit.hasBlank(str, sourceCharset, destCharset)) { return str; } try { return new String(str.getBytes(sourceCharset), destCharset); } catch (UnsupportedEncodingException e) { return str; } } /** * 数字金额大写转换 先写个完整的然后将如零拾替换成零 * * @param n 数字 * @return 中文大写数字 */ public static String digitUppercase(double n) { String fraction[] = { "角", "分" }; String digit[] = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; String unit[][] = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; String head = n < 0 ? "负" : ""; n = Math.abs(n); String s = ""; for (int i = 0; i < fraction.length; i++) { s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); } if (s.length() < 1) { s = "整"; } int integerPart = (int) Math.floor(n); for (int i = 0; i < unit[0].length && integerPart > 0; i++) { String p = ""; for (int j = 0; j < unit[1].length && n > 0; j++) { p = digit[integerPart % 10] + unit[1][j] + p; integerPart = integerPart / 10; } s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; } return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/CryptUtils.java ================================================ package cn.enilu.material.utils; import cn.enilu.material.bean.constant.Const; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import java.io.*; import java.security.MessageDigest; import java.security.SecureRandom; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Base64; import java.util.Date; /** * 加密工具类 * @author enilu */ public class CryptUtils { private static final String DES = "DES"; /** * 加密 * * @param src 数据源 * @param key 密钥,长度必须是8的倍数 * @return 返回加密后的数据 * @throws Exception */ private static byte[] encrypt(byte[] src, byte[] key) { // DES算法要求有一个可信任的随机数源 try { SecureRandom sr = new SecureRandom(); // 从原始密匙数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密匙工厂,然后用它把DESKeySpec转换成 // 一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(DES); // 用密匙初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); // 现在,获取数据并加密 // 正式执行加密操作 return cipher.doFinal(src); } catch (Exception e) { throw new RuntimeException(e); } } /** * 解密 * * @param src 数据源 * @param key 密钥,长度必须是8的倍数 * @return 返回解密后的原始数据 * @throws Exception */ private static byte[] decrypt(byte[] src, byte[] key) { try { // DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密匙数据创建一个DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成 // 一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成解密操作 Cipher cipher = Cipher.getInstance(DES); // 用密匙初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, securekey, sr); // 现在,获取数据并解密 // 正式执行解密操作 return cipher.doFinal(src); } catch (Exception e) { throw new RuntimeException(e); } } /** * 数据解密 * * @param data * @param key 密钥 * @return String * @throws Exception */ private final static String decrypt(String data, String key) { if (data != null) { return new String(decrypt(hex2byte(data.getBytes()), key.getBytes())); } return null; } /** * 数据加密 * * @param data * @param key 密钥 * @return * @throws Exception */ public final static String encrypt(String data, String key) { if (data != null) { try { return byte2hex(encrypt(data.getBytes(), key.getBytes())); } catch (Exception e) { throw new RuntimeException(e); } } return null; } /** * 二行制转字符串 * * @param b * @return */ private static String byte2hex(byte[] b) { StringBuilder hs = new StringBuilder(); String stmp; for (int n = 0; b != null && n < b.length; n++) { stmp = Integer.toHexString(b[n] & 0XFF); if (stmp.length() == 1) { hs.append('0'); } hs.append(stmp); } return hs.toString().toUpperCase(); } private static byte[] hex2byte(byte[] b) { if ((b.length % 2) != 0) { throw new IllegalArgumentException(); } byte[] b2 = new byte[b.length / 2]; for (int n = 0; n < b.length; n += 2) { String item = new String(b, n, 2); b2[n / 2] = (byte) Integer.parseInt(item, 16); } return b2; } /** * 对字符串按密钥进行DES加密 */ public static String encode(String data) { return encrypt(data, Const.CRYPT_DES_KEY); } /** * 对字符串按密钥进行DES解密 */ public static String decode(String data) { return decrypt(data, Const.CRYPT_DES_KEY); } /** * 对字符串按密钥进行DES加密 */ public static String encode(String data, String key) { if (data != null && key != null) { try { SecretKey secretKey = generateSecretKey(key); Cipher cipher = Cipher.getInstance(DES); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return encryptBASE64(cipher.doFinal(data.getBytes())); } catch (Exception e) { throw new RuntimeException(e); } } return null; } /** * 对字符串按密钥进行DES解密 */ public static String decode(String data, String key) { if (data != null && key != null) { try { SecretKey secretKey = generateSecretKey(key); Cipher cipher = Cipher.getInstance(DES); // Get the cipher cipher.init(Cipher.DECRYPT_MODE, secretKey); return new String(cipher.doFinal(decryptBASE64(data))); } catch (Exception e) { throw new RuntimeException(e); } } return null; } /** * 根据密码和keyGenerator生成密钥。 * @param key * @return */ private static SecretKey generateSecretKey(String key) { SecretKey secretKey = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = key.getBytes(); md.update(bytes, 0, bytes.length); // Generate 16 bytes byte[] mdBytes = md.digest(); // Fetch 8 bytes for DESKeySpec byte[] truncatedBytes = Arrays.copyOf(mdBytes, 8); DESKeySpec keySpec = new DESKeySpec(truncatedBytes); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); secretKey = keyFactory.generateSecret(keySpec); } catch (Exception e) { e.printStackTrace(); } return secretKey; } /** * 转换成字符串 */ public static String convertToHexString(byte[] data) { StringBuffer strBuffer = new StringBuffer(); for (int i = 0; i < data.length; i++) { String hex = Integer.toHexString(0xff & data[i]); if (hex.length() == 1) { hex = '0' + hex; } strBuffer.append(hex.toUpperCase()); } return strBuffer.toString(); } /** * BASE64加密 */ public static String encryptBASE64(byte[] key) throws Exception { return (Base64.getEncoder()).encodeToString(key); } /** * BASE64解密 */ public static byte[] decryptBASE64(String key) throws Exception { return Base64.getDecoder().decode(key); } public static String encodeBASE64(byte[] bytes) { String encode = Base64.getEncoder().encodeToString(bytes); encode = encode.replaceAll("\n", ""); return encode; } /** * 文件内容生成BASE64编码的字符串 */ public static String encodeBASE64(File file) { Base64.Encoder encoder = Base64.getEncoder(); StringBuilder sb = new StringBuilder(); InputStream input = null; try { input = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] temp = new byte[1024]; for (int len = input.read(temp); len != -1; len = input.read(temp)) { out.write(temp, 0, len); sb.append(encoder.encode(out.toByteArray())); out.reset(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (input != null) { input.close(); } } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } /** * BASE64编码的字符串解码为文件 */ public static void decodeBASE64(String encoder, File file) { Base64.Decoder decoder = Base64.getDecoder(); FileOutputStream fos = null; try { fos = new FileOutputStream(file); byte[] decoderBytes = decoder.decode(encoder); fos.write(decoderBytes); fos.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } public static byte[] decodeBASE64(String encoder) { Base64.Decoder decoder = Base64.getDecoder(); return decoder.decode(encoder); } public static String getSign(String privateKey) { return getSign(privateKey, new Date()); } public static String getMD5ofStr(String inStr) { MessageDigest md5 = null; try { // 获得MD5摘要算法的 MessageDigest 对象 md5 = MessageDigest.getInstance("MD5"); byte[] byteArray = inStr.getBytes("UTF-8"); // 获得密文 byte[] md5Bytes = md5.digest(byteArray); // 把密文转换成十六进制的字符串形式 StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString().toUpperCase(); } catch (Exception e) { e.printStackTrace(); } return ""; } public static String getSign(String privateKey, Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String sign = getMD5ofStr(privateKey + sdf.format(date)); return sign; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/DateTime.java ================================================ package cn.enilu.material.utils; import java.util.Date; /** * 封装java.util.Date * @author xiaoleilu * */ public class DateTime extends Date{ private static final long serialVersionUID = -5395712593979185936L; /** * 转换JDK date为 DateTime * @param date JDK Date * @return DateTime */ public static DateTime parse(Date date) { return new DateTime(date); } /** * 当前时间 */ public DateTime() { super(); } /** * 给定日期的构造 * @param date 日期 */ public DateTime(Date date) { this(date.getTime()); } /** * 给定日期毫秒数的构造 * @param timeMillis 日期毫秒数 */ public DateTime(long timeMillis) { super(timeMillis); } @Override public String toString() { return DateTimeKit.formatDateTime(this); } public String toString(String format) { return DateTimeKit.format(this, format); } /** * @return 输出精确到毫秒的标准日期形式 */ public String toMsStr() { return DateTimeKit.format(this, DateTimeKit.NORM_DATETIME_MS_PATTERN); } /** * @return java.util.Date */ public Date toDate() { return new Date(this.getTime()); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/DateTimeKit.java ================================================ package cn.enilu.material.utils; import cn.enilu.material.utils.cache.exception.ToolBoxException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashSet; import java.util.Locale; /** * 时间工具类 * @author xiaoleilu */ public class DateTimeKit { /** 毫秒 */ public final static long MS = 1; /** 每秒钟的毫秒数 */ public final static long SECOND_MS = MS * 1000; /** 每分钟的毫秒数 */ public final static long MINUTE_MS = SECOND_MS * 60; /** 每小时的毫秒数 */ public final static long HOUR_MS = MINUTE_MS * 60; /** 每天的毫秒数 */ public final static long DAY_MS = HOUR_MS * 24; /** 标准日期格式 */ public final static String NORM_DATE_PATTERN = "yyyy-MM-dd"; /** 标准时间格式 */ public final static String NORM_TIME_PATTERN = "HH:mm:ss"; /** 标准日期时间格式,精确到分 */ public final static String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm"; /** 标准日期时间格式,精确到秒 */ public final static String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; /** 标准日期时间格式,精确到毫秒 */ public final static String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; /** HTTP头中日期时间格式 */ public final static String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; /** 标准日期(不含时间)格式化器 */ // private final static SimpleDateFormat NORM_DATE_FORMAT = new SimpleDateFormat(NORM_DATE_PATTERN); private static ThreadLocal NORM_DATE_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_DATE_PATTERN); }; }; /** 标准时间格式化器 */ // private final static SimpleDateFormat NORM_TIME_FORMAT = new SimpleDateFormat(NORM_TIME_PATTERN); private static ThreadLocal NORM_TIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_TIME_PATTERN); }; }; /** 标准日期时间格式化器 */ // private final static SimpleDateFormat NORM_DATETIME_FORMAT = new SimpleDateFormat(NORM_DATETIME_PATTERN); private static ThreadLocal NORM_DATETIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_DATETIME_PATTERN); }; }; /** HTTP日期时间格式化器 */ // private final static SimpleDateFormat HTTP_DATETIME_FORMAT = new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US); private static ThreadLocal HTTP_DATETIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US); }; }; /** * 当前时间,格式 yyyy-MM-dd HH:mm:ss * * @return 当前时间的标准形式字符串 */ public static String now() { return formatDateTime(new DateTime()); } /** * 当前时间long * * @param isNano 是否为高精度时间 * @return 时间 */ public static long current(boolean isNano) { return isNano ? System.nanoTime() : System.currentTimeMillis(); } /** * 当前日期,格式 yyyy-MM-dd * * @return 当前日期的标准形式字符串 */ public static String today() { return formatDate(new DateTime()); } /** * @return 当前月份 */ public static int thisMonth() { return month(date()); } /** * @return 今年 */ public static int thisYear() { return year(date()); } /** * @return 当前时间 */ public static DateTime date() { return new DateTime(); } /** * Long类型时间转为Date * * @param date Long类型Date(Unix时间戳) * @return 时间对象 */ public static DateTime date(long date) { return new DateTime(date); } /** * 转换为Calendar对象 * * @param date 日期对象 * @return Calendar对象 */ public static Calendar toCalendar(Date date) { final Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal; } /** * 获得月份,从1月开始计数 * * @param date 日期 * @return 月份 */ public static int month(Date date) { return toCalendar(date).get(Calendar.MONTH) + 1; } /** * 获得年 * * @param date 日期 * @return 年 */ public static int year(Date date) { return toCalendar(date).get(Calendar.YEAR); } /** * 获得季节 * * @param date 日期 * @return 第几个季节 */ public static int season(Date date) { return toCalendar(date).get(Calendar.MONTH) / 3 + 1; } /** * 获得指定日期年份和季节
* 格式:[20131]表示2013年第一季度 * * @param date 日期 * @return Season ,类似于 20132 */ public static String yearAndSeason(Date date) { return yearAndSeason(toCalendar(date)); } /** * 获得指定日期区间内的年份和季节
* * @param startDate 其实日期(包含) * @param endDate 结束日期(包含) * @return Season列表 ,元素类似于 20132 */ public static LinkedHashSet yearAndSeasons(Date startDate, Date endDate) { final LinkedHashSet seasons = new LinkedHashSet(); if (startDate == null || endDate == null) { return seasons; } final Calendar cal = Calendar.getInstance(); cal.setTime(startDate); while (true) { // 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环 if (startDate.after(endDate)) { startDate = endDate; } seasons.add(yearAndSeason(cal)); if (startDate.equals(endDate)) { break; } cal.add(Calendar.MONTH, 3); startDate = cal.getTime(); } return seasons; } // ------------------------------------ Format start ---------------------------------------------- /** * 根据特定格式格式化日期 * * @param date 被格式化的日期 * @param format 格式 * @return 格式化后的字符串 */ public static String format(Date date, String format) { return new SimpleDateFormat(format).format(date); } /** * 格式 yyyy-MM-dd HH:mm:ss * * @param date 被格式化的日期 * @return 格式化后的日期 */ public static String formatDateTime(Date date) { if(null == date){ return null; } return NORM_DATETIME_FORMAT.get().format(date); } /** * 格式 yyyy-MM-dd * * @param date 被格式化的日期 * @return 格式化后的字符串 */ public static String formatDate(Date date) { if(null == date){ return null; } return NORM_DATE_FORMAT.get().format(date); } /** * 格式化为Http的标准日期格式 * * @param date 被格式化的日期 * @return HTTP标准形式日期字符串 */ public static String formatHttpDate(Date date) { if(null == date){ return null; } return HTTP_DATETIME_FORMAT.get().format(date); } // ------------------------------------ Format end ---------------------------------------------- // ------------------------------------ Parse start ---------------------------------------------- /** * 构建DateTime对象 * * @param dateStr Date字符串 * @param simpleDateFormat 格式化器 * @return DateTime对象 */ public static DateTime parse(String dateStr, SimpleDateFormat simpleDateFormat) { try { return new DateTime(simpleDateFormat.parse(dateStr)); } catch (Exception e) { throw new ToolBoxException(StrKit.format("Parse [{}] with format [{}] error!", dateStr, simpleDateFormat.toPattern()), e); } } /** * 将特定格式的日期转换为Date对象 * * @param dateString 特定格式的日期 * @param format 格式,例如yyyy-MM-dd * @return 日期对象 */ public static DateTime parse(String dateString, String format) { return parse(dateString, new SimpleDateFormat(format)); } /** * 格式yyyy-MM-dd HH:mm:ss * * @param dateString 标准形式的时间字符串 * @return 日期对象 */ public static DateTime parseDateTime(String dateString) { return parse(dateString, NORM_DATETIME_FORMAT.get()); } /** * 格式yyyy-MM-dd * * @param dateString 标准形式的日期字符串 * @return 日期对象 */ public static DateTime parseDate(String dateString) { return parse(dateString, NORM_DATE_FORMAT.get()); } /** * 格式HH:mm:ss * * @param timeString 标准形式的日期字符串 * @return 日期对象 */ public static DateTime parseTime(String timeString) { return parse(timeString, NORM_TIME_FORMAT.get()); } /** * 格式:
* 1、yyyy-MM-dd HH:mm:ss
* 2、yyyy-MM-dd
* 3、HH:mm:ss
* 4、yyyy-MM-dd HH:mm 5、yyyy-MM-dd HH:mm:ss.SSS * * @param dateStr 日期字符串 * @return 日期 */ public static DateTime parse(String dateStr) { if (null == dateStr) { return null; } dateStr = dateStr.trim(); int length = dateStr.length(); try { if (length == NORM_DATETIME_PATTERN.length()) { return parseDateTime(dateStr); } else if (length == NORM_DATE_PATTERN.length()) { return parseDate(dateStr); } else if (length == NORM_TIME_PATTERN.length()) { return parseTime(dateStr); } else if (length == NORM_DATETIME_MINUTE_PATTERN.length()) { return parse(dateStr, NORM_DATETIME_MINUTE_PATTERN); } else if (length >= NORM_DATETIME_MS_PATTERN.length() - 2) { return parse(dateStr, NORM_DATETIME_MS_PATTERN); } } catch (Exception e) { throw new ToolBoxException(StrKit.format("Parse [{}] with format normal error!", dateStr)); } // 没有更多匹配的时间格式 throw new ToolBoxException(StrKit.format(" [{}] format is not fit for date pattern!", dateStr)); } // ------------------------------------ Parse end ---------------------------------------------- // ------------------------------------ Offset start ---------------------------------------------- /** * 获取某天的开始时间 * * @param date 日期 * @return 某天的开始时间 */ public static DateTime getBeginTimeOfDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); return new DateTime(calendar.getTime()); } /** * 获取某天的结束时间 * * @param date 日期 * @return 某天的结束时间 */ public static DateTime getEndTimeOfDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); calendar.set(Calendar.MILLISECOND, 999); return new DateTime(calendar.getTime()); } /** * 昨天 * * @return 昨天 */ public static DateTime yesterday() { return offsiteDay(new DateTime(), -1); } /** * 上周 * * @return 上周 */ public static DateTime lastWeek() { return offsiteWeek(new DateTime(), -1); } /** * 上个月 * * @return 上个月 */ public static DateTime lastMouth() { return offsiteMonth(new DateTime(), -1); } /** * 偏移天 * * @param date 日期 * @param offsite 偏移天数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteDay(Date date, int offsite) { return offsiteDate(date, Calendar.DAY_OF_YEAR, offsite); } /** * 偏移周 * * @param date 日期 * @param offsite 偏移周数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteWeek(Date date, int offsite) { return offsiteDate(date, Calendar.WEEK_OF_YEAR, offsite); } /** * 偏移月 * * @param date 日期 * @param offsite 偏移月数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteMonth(Date date, int offsite) { return offsiteDate(date, Calendar.MONTH, offsite); } /** * 获取指定日期偏移指定时间后的时间 * * @param date 基准日期 * @param calendarField 偏移的粒度大小(小时、天、月等)使用Calendar中的常数 * @param offsite 偏移量,正数为向后偏移,负数为向前偏移 * @return 偏移后的日期 */ public static DateTime offsiteDate(Date date, int calendarField, int offsite) { Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(calendarField, offsite); return new DateTime(cal.getTime()); } // ------------------------------------ Offset end ---------------------------------------------- /** * 判断两个日期相差的时长
* 返回 minuend - subtrahend 的差 * * @param subtrahend 减数日期 * @param minuend 被减数日期 * @param diffField 相差的选项:相差的天、小时 * @return 日期差 */ public static long diff(Date subtrahend, Date minuend, long diffField) { long diff = minuend.getTime() - subtrahend.getTime(); return diff / diffField; } /** * 计时,常用于记录某段代码的执行时间,单位:纳秒 * * @param preTime 之前记录的时间 * @return 时间差,纳秒 */ public static long spendNt(long preTime) { return System.nanoTime() - preTime; } /** * 计时,常用于记录某段代码的执行时间,单位:毫秒 * * @param preTime 之前记录的时间 * @return 时间差,毫秒 */ public static long spendMs(long preTime) { return System.currentTimeMillis() - preTime; } /** * 格式化成yyMMddHHmm后转换为int型 * * @param date 日期 * @return int */ public static int toIntSecond(Date date) { return Integer.parseInt(format(date, "yyMMddHHmm")); } /** * 计算指定指定时间区间内的周数 * * @param start 开始时间 * @param end 结束时间 * @return 周数 */ public static int weekCount(Date start, Date end) { final Calendar startCalendar = Calendar.getInstance(); startCalendar.setTime(start); final Calendar endCalendar = Calendar.getInstance(); endCalendar.setTime(end); final int startWeekofYear = startCalendar.get(Calendar.WEEK_OF_YEAR); final int endWeekofYear = endCalendar.get(Calendar.WEEK_OF_YEAR); int count = endWeekofYear - startWeekofYear + 1; if (Calendar.SUNDAY != startCalendar.get(Calendar.DAY_OF_WEEK)) { count--; } return count; } /** * 计时器
* 计算某个过程话费的时间,精确到毫秒 * * @return Timer */ public static Timer timer() { return new Timer(); } /** * 生日转为年龄,计算法定年龄 * @param birthDay 生日,标准日期字符串 * @return 年龄 * @throws Exception */ public static int ageOfNow(String birthDay) { return ageOfNow(parse(birthDay)); } /** * 生日转为年龄,计算法定年龄 * @param birthDay 生日 * @return 年龄 * @throws Exception */ public static int ageOfNow(Date birthDay) { return age(birthDay,date()); } /** * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄 * @param birthDay 生日 * @param dateToCompare 需要对比的日期 * @return 年龄 * @throws Exception */ public static int age(Date birthDay, Date dateToCompare) { Calendar cal = Calendar.getInstance(); cal.setTime(dateToCompare); if (cal.before(birthDay)) { throw new IllegalArgumentException(StrKit.format("Birthday is after date {}!", formatDate(dateToCompare))); } int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH); int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); cal.setTime(birthDay); int age = year - cal.get(Calendar.YEAR); int monthBirth = cal.get(Calendar.MONTH); if (month == monthBirth) { int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH); if (dayOfMonth < dayOfMonthBirth) { //如果生日在当月,但是未达到生日当天的日期,年龄减一 age--; } } else if (month < monthBirth){ //如果当前月份未达到生日的月份,年龄计算减一 age--; } return age; } /** * 计时器
* 计算某个过程话费的时间,精确到毫秒 * * @author Looly * */ public static class Timer { private long time; private boolean isNano; public Timer() { this(false); } public Timer(boolean isNano) { this.isNano = isNano; start(); } /** * @return 开始计时并返回当前时间 */ public long start() { time = current(isNano); return time; } /** * @return 重新计时并返回从开始到当前的持续时间 */ public long durationRestart() { long now = current(isNano); long d = now - time; time = now; return d; } /** * @return 从开始到当前的持续时间 */ public long duration() { return current(isNano) - time; } } // ------------------------------------------------------------------------ Private method start /** * 获得指定日期年份和季节
* 格式:[20131]表示2013年第一季度 * * @param cal 日期 */ private static String yearAndSeason(Calendar cal) { return new StringBuilder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH) / 3 + 1).toString(); } // ------------------------------------------------------------------------ Private method end } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/DateUtil.java ================================================ /** * Copyright (c) 2015-2016, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.utils; import org.apache.commons.lang3.time.DateFormatUtils; import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; public class DateUtil { private static final Object lock = new Object(); private static final Map> pool = new HashMap>(); /** * 获取YYYY格式 * * @return */ public static String getYear() { return formatDate(new Date(), "yyyy"); } /** * 获取YYYY格式 * * @return */ public static String getYear(Date date) { return formatDate(date, "yyyy"); } /** * 获取YYYY-MM-DD格式 * * @return */ public static String getDay() { return formatDate(new Date(), "yyyy-MM-dd"); } /** * 获取YYYY-MM-DD格式 * * @return */ public static String getDay(Date date) { return formatDate(date, "yyyy-MM-dd"); } /** * 获取YYYYMMDD格式 * * @return */ public static String getDays() { return formatDate(new Date(), "yyyyMMdd"); } /** * 获取YYYYMMDD格式 * * @return */ public static String getDays(Date date) { return formatDate(date, "yyyyMMdd"); } /** * 获取YYYY-MM-DD HH:mm:ss格式 * * @return */ public static String getTime() { return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"); } /** * 获取YYYY-MM-DD HH:mm:ss.SSS格式 * * @return */ public static String getMsTime() { return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"); } /** * 获取YYYYMMDDHHmmss格式 * * @return */ public static String getAllTime() { return formatDate(new Date(), "yyyyMMddHHmmss"); } /** * 获取YYYY-MM-DD HH:mm:ss格式 * * @return */ public static String getTime(Date date) { return formatDate(date, "yyyy-MM-dd HH:mm:ss"); } public static String formatDate(Date date, String pattern) { String formatDate = null; if (StringUtils.isNotEmpty(pattern)) { formatDate = DateFormatUtils.format(date, pattern); } else { formatDate = DateFormatUtils.format(date, "yyyy-MM-dd"); } return formatDate; } /** * @Title: compareDate * @Description:(日期比较,如果s>=e 返回true 否则返回false) * @param s * @param e * @return boolean * @throws * @author luguosui */ public static boolean compareDate(String s, String e) { if (parseDate(s) == null || parseDate(e) == null) { return false; } return parseDate(s).getTime() >= parseDate(e).getTime(); } /** * 格式化日期 * * @return */ public static Date parseDate(String date) { return parse(date,"yyyy-MM-dd"); } /** * 格式化日期 * * @return */ public static Date parseTime(String date) { return parse(date,"yyyy-MM-dd HH:mm:ss"); } /** * 格式化日期 * * @return */ public static Date parse(String date, String pattern) { if (StringUtils.isNotEmpty(date)) { if (pattern == null || "".equals(pattern)) { return null; } DateFormat format = getDFormat(pattern); try { return format.parse(date); } catch (ParseException e) { e.printStackTrace(); } } return null; } public static SimpleDateFormat getDFormat(String pattern) { ThreadLocal tl = pool.get(pattern); if (tl == null) { synchronized (lock) { tl = pool.get(pattern); if (tl == null) { final String p = pattern; tl = new ThreadLocal() { @Override protected synchronized SimpleDateFormat initialValue() { return new SimpleDateFormat(p); } }; pool.put(p, tl); } } } return tl.get(); } /** * 格式化日期 * * @return */ public static String format(Date date, String pattern) { return DateFormatUtils.format(date, pattern); } /** * 把日期转换为Timestamp * * @param date * @return */ public static Timestamp format(Date date) { return new Timestamp(date.getTime()); } /** * 校验日期是否合法 * * @return */ public static boolean isValidDate(String s) { return parse(s, "yyyy-MM-dd HH:mm:ss") != null; } /** * 校验日期是否合法 * * @return */ public static boolean isValidDate(String s, String pattern) { return parse(s, pattern) != null; } public static int getDiffYear(String startTime, String endTime) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { int years = (int) (((fmt.parse(endTime).getTime() - fmt.parse( startTime).getTime()) / (1000 * 60 * 60 * 24)) / 365); return years; } catch (Exception e) { // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 return 0; } } /** *
  • 功能描述:时间相减得到天数 * * @param beginDateStr * @param endDateStr * @return long * @author Administrator */ public static long getDaySub(String beginDateStr, String endDateStr) { long day = 0; SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd"); Date beginDate = null; Date endDate = null; try { beginDate = format.parse(beginDateStr); endDate = format.parse(endDateStr); } catch (ParseException e) { e.printStackTrace(); } day = (endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000); // System.out.println("相隔的天数="+day); return day; } /** * 得到n天之后的日期 * * @param days * @return */ public static String getAfterDayDate(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdfd.format(date); return dateStr; } /** * 得到n天之后是周几 * * @param days * @return */ public static String getAfterDayWeek(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdf = new SimpleDateFormat("E"); String dateStr = sdf.format(date); return dateStr; } public static void main(String[] args) { System.out.println(getTime(new Date())); System.out.println(getAfterDayWeek("3")); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/HexKit.java ================================================ package cn.enilu.material.utils; import java.nio.charset.Charset; /** * 十六进制(简写为hex或下标16)在数学中是一种逢16进1的进位制,一般用数字0到9和字母A到F表示(其中:A~F即10~15)。
    * 例如十进制数57,在二进制写作111001,在16进制写作39。
    * 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20
    * * 参考:https://my.oschina.net/xinxingegeya/blog/287476 * * @author Looly * */ public class HexKit { /** * 用于建立十六进制字符的输出的小写字符数组 */ private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * 用于建立十六进制字符的输出的大写字符数组 */ private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; //---------------------------------------------------------------------------------------------------- encode /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @return 十六进制char[] */ public static char[] encodeHex(byte[] data) { return encodeHex(data, true); } /** * 将字节数组转换为十六进制字符数组 * * @param str 字符串 * @param charset 编码 * @return 十六进制char[] */ public static char[] encodeHex(String str, Charset charset) { return encodeHex(StrKit.getBytes(str, charset), true); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 * @return 十六进制char[] */ public static char[] encodeHex(byte[] data, boolean toLowerCase) { return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @return 十六进制String */ public static String encodeHexStr(byte[] data) { return encodeHexStr(data, true); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 * @return 十六进制String */ public static String encodeHexStr(byte[] data, boolean toLowerCase) { return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } //---------------------------------------------------------------------------------------------------- decode /** * 将十六进制字符数组转换为字符串 * * @param hexStr 十六进制String * @param charset 编码 * @return 字符串 */ public static String decodeHexStr(String hexStr, Charset charset) { if(StrKit.isEmpty(hexStr)){ return hexStr; } return decodeHexStr(hexStr.toCharArray(), charset); } /** * 将十六进制字符数组转换为字符串 * * @param hexData 十六进制char[] * @param charset 编码 * @return 字符串 */ public static String decodeHexStr(char[] hexData, Charset charset) { return StrKit.str(decodeHex(hexData), charset); } /** * 将十六进制字符数组转换为字节数组 * * @param hexData 十六进制char[] * @return byte[] * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 */ public static byte[] decodeHex(char[] hexData) { int len = hexData.length; if ((len & 0x01) != 0) { throw new RuntimeException("Odd number of characters."); } byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(hexData[j], j) << 4; j++; f = f | toDigit(hexData[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } //---------------------------------------------------------------------------------------- Private method start /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制String */ private static String encodeHexStr(byte[] data, char[] toDigits) { return new String(encodeHex(data, toDigits)); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制char[] */ private static char[] encodeHex(byte[] data, char[] toDigits) { int l = data.length; char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return out; } /** * 将十六进制字符转换成一个整数 * * @param ch 十六进制char * @param index 十六进制字符在字符数组中的位置 * @return 一个整数 * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 */ private static int toDigit(char ch, int index) { int digit = Character.digit(ch, 16); if (digit == -1) { throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } //---------------------------------------------------------------------------------------- Private method end /** * 2进制转16进制 * @param bString 2进制字符串 * @return */ public static String binary2Hex(String bString) { if (bString == null || bString.equals("") || bString.length() % 8 != 0) return null; StringBuffer tmp = new StringBuffer(); int iTmp = 0; for (int i = 0; i < bString.length(); i += 4) { iTmp = 0; for (int j = 0; j < 4; j++) { iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1); } tmp.append(Integer.toHexString(iTmp)); } return tmp.toString(); } /** * 16进制转2进制 * @param hexString * @return */ public static String hex2Binary(String hexString) { if (hexString == null || hexString.length() % 2 != 0) return null; String bString = "", tmp; for (int i = 0; i < hexString.length(); i++) { tmp = "0000" + Integer.toBinaryString(Integer.parseInt(hexString.substring(i, i + 1), 16)); bString += tmp.substring(tmp.length() - 4); } return bString; } /** * 将二进制转换成16进制 * @param buf * @return */ public static String binary2Hex(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /** * 将16进制转换为二进制 * @param hexStr * @return */ public static byte[] hex2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++) { int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); result[i] = (byte) (high * 16 + low); } return result; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/HttpKit.java ================================================ /** * Copyright (c) 2015-2016, Chill Zhuang 庄骞 (smallchill@163.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.utils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map; public class HttpKit { public static String getIp(){ return HttpKit.getRequest().getRemoteHost(); } /** * 获取所有请求的值 */ public static Map getRequestParameters() { HashMap values = new HashMap<>(); HttpServletRequest request = HttpKit.getRequest(); Enumeration enums = request.getParameterNames(); while ( enums.hasMoreElements()){ String paramName = (String) enums.nextElement(); String paramValue = request.getParameter(paramName); values.put(paramName, paramValue); } return values; } /** * 获取 HttpServletRequest */ public static HttpServletResponse getResponse() { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); return response; } /** * 获取 包装防Xss Sql注入的 HttpServletRequest * @return request */ public static HttpServletRequest getRequest() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); return new WafRequestWrapper(request); } public static String getToken(){ return getRequest().getHeader("Authorization"); } /** * 向指定URL发送GET方法的请求 * * @param url 发送请求的URL * @param param 请求参数 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, Map param) { String result = ""; BufferedReader in = null; try { StringBuffer query = new StringBuffer(); for (Map.Entry kv : param.entrySet()) { query.append(URLEncoder.encode(kv.getKey(), "UTF-8") + "="); query.append(URLEncoder.encode(kv.getValue(), "UTF-8") + "&"); } if (query.lastIndexOf("&") > 0) { query.deleteCharAt(query.length() - 1); } String urlNameString = url + "?" + query.toString(); URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @param param 请求参数 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, Map param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { String para = ""; for (String key : param.keySet()) { para += (key + "=" + param.get(key) + "&"); } if (para.lastIndexOf("&") > 0) { para = para.substring(0, para.length() - 1); } String urlNameString = url + "?" + para; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); int status = conn.getResponseCode(); InputStream inputStream = null; if (status != HttpURLConnection.HTTP_OK){ inputStream = conn.getErrorStream(); }else{ inputStream = conn.getInputStream(); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/Lists.java ================================================ package cn.enilu.material.utils; import net.sf.ehcache.hibernate.management.impl.BeanUtils; import java.util.*; /** * 集合工具类 * * @author enilu */ public final class Lists { private Lists() { } /** * 碾平集合咯,主要针对集合元素为集合的情况有效果 * * @param list * @param * @return */ @SuppressWarnings("unchecked") public static List flatten(List list) { List result = new ArrayList(); for (Object o : list) { if (o instanceof List) { List subResult = flatten((List) o); result.addAll(subResult); } else { result.add((T) o); } } return result; } /** * 压缩集合,去掉集合中的null记录 * * @param list * @param * @return */ public static List compact(List list) { List result = new ArrayList(); for (T t : list) { if (t != null) { result.add(t); } } return result; } /** * 提取集合中指定属性值 * * @param list * @param property * @param * @param * @return */ public static List map(List list, String property) { List result = new ArrayList(); for (T t : list) { try { R r = (R) getProperty(t, property); result.add(r); } catch (Exception e) { e.printStackTrace(); } } return result; } /** * 将集合转换为map * * @param list * @param keyProperty * @param * @param * @return */ public static Map toMap(List list, String keyProperty) { Map map = new HashMap(); for (V v : list) { try { K k = (K) getProperty(v, keyProperty); map.put(k, v); } catch (Exception e) { e.printStackTrace(); } } return map; } /** * 移除与value不想等的值,原集合不发生变化 * * @param list * @param property * @param value * @param * @return */ public static List filter(List list, String property, Object value) { List result = new ArrayList(); for (T t : list) { try { Object v = getProperty(t, property); if ((v == null && value == null) || (v != null && v.equals(value))) { result.add(t); } } catch (Exception e) { e.printStackTrace(); } } return result; } /** * 移除与value相等的值, 原数组不发生变化. * * @param list * @param value * @return */ public static List without(List list, T value) { List result = new ArrayList(); for (T t : list) { if ((value == null && t == null) || (value != null && value.equals(t))) { continue; } result.add(t); } return result; } /** * 对集合去重,原集合不发生变化 * * @param input * @param * @return */ public static List uniq(List input) { LinkedHashMap map = new LinkedHashMap<>(); for (T t : input) { map.put(t, t); } return new ArrayList(map.values()); } /** * 按照给定的集合(keys)进行排序 * * @param input 要排序的集合 * @param keyProperty 排序的属性 * @param keys 给定的键值集合 * @param * @param * @return */ public static List sortBy(List input, String keyProperty, List keys) { if (input.isEmpty()) { return new ArrayList(); } Map map = toMap(input, keyProperty); List result = new ArrayList(); for (K k : keys) { T t = map.get(k); if (t != null) { result.add(t); } } return result; } /** * 对集合进行分组 * * @param input * @param keyProperty * @param * @param * @return */ public static Map> group(List input, String keyProperty) { Map> result = new HashMap<>(); for (V v : input) { try { K k = (K) getProperty(v, keyProperty); List list = result.get(k); if (list == null) { list = new ArrayList<>(); result.put(k, list); } list.add(v); } catch (Exception e) { e.printStackTrace(); } } return result; } /** * 将数组按n个一份拆分. * * @param * @param input * @param n * @return */ public static List> group(List input, int n) { if (n <= 0) { throw new IllegalArgumentException("n must > 0"); } int size = input.size(); int m = (size + n - 1) / n; List> result = new ArrayList<>(m); for (int i = 0; i < m; i++) { List items = new ArrayList(n); int end = i < m - 1 ? n : size - i * n; for (int j = 0; j < end; j++) { items.add(input.get(i * n + j)); } result.add(items); } return result; } public static boolean containAny(Set parent, Set child) { if (parent == null || child == null) { return false; } Iterator iter = child.iterator(); while (iter.hasNext()) { return parent.contains(iter.next()); } return false; } public static List newArrayList(V... vs) { List list = new ArrayList(); for (V v : vs) { list.add(v); } return list; } private static Object getProperty(Object bean,String name){ return BeanUtils.getBeanProperty(bean,name); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/Log.java ================================================ package cn.enilu.material.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author deanyule */ public class Log { public static Logger get(Class clazz) { return LoggerFactory.getLogger(clazz); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/MD5.java ================================================ package cn.enilu.material.utils; import com.google.common.base.Strings; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * MD5加密工具类 */ public class MD5 { public static final Logger LOG = LoggerFactory.getLogger(MD5.class); /** * 16进制字符集 */ private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /** * 循环次数 */ public final static int HASH_ITERATIONS = 1024; /** * 加盐参数 */ public final static String HASH_ALGORITHM_NAME = "MD5"; /** * * MD5加密字符串 * * @param str 目标字符串 * @return MD5加密后的字符串 */ public static String getMD5String(String str) { if (Strings.isNullOrEmpty(str)) { return null; } return getMD5String(str.getBytes()); } /** * * MD5加密以byte数组表示的字符串 * * @param bytes 目标byte数组 * @return MD5加密后的字符串 */ public static String getMD5String(byte[] bytes) { try { MessageDigest MESSAGE_DIGEST = MessageDigest.getInstance(HASH_ALGORITHM_NAME); MESSAGE_DIGEST.update(bytes); return bytesToHex(MESSAGE_DIGEST.digest()); }catch (Exception e){ LOG.error(e.getMessage(), e); return null; } } /** * 获取文件的MD5值 * * @param file 目标文件 * @return MD5字符串 */ public static String getFileMD5String(File file) { String ret = ""; FileInputStream in = null; FileChannel ch = null; try { MessageDigest MESSAGE_DIGEST = MessageDigest.getInstance(HASH_ALGORITHM_NAME); in = new FileInputStream(file); ch = in.getChannel(); ByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); MESSAGE_DIGEST.update(byteBuffer); ret = bytesToHex(MESSAGE_DIGEST.digest()); } catch (Exception e) { LOG.error(e.getMessage(), e); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(ch); } return ret; } /** * * 获取文件的MD5值 * * @param fileName 目标文件的完整名称 * @return MD5字符串 */ public static String getFileMD5String(String fileName) { return getFileMD5String(new File(fileName)); } /** * * 将字节数组转换成16进制字符串 * * @param bytes 目标字节数组 * @return 转换结果 */ public static String bytesToHex(byte[] bytes) { return bytesToHex(bytes, 0, bytes.length); } /** * * 将字节数组中指定区间的子数组转换成16进制字符串 * * @param bytes 目标字节数组 * @param start 起始位置(包括该位置) * @param end 结束位置(不包括该位置) * @return 转换结果 */ public static String bytesToHex(byte[] bytes, int start, int end) { StringBuilder sb = new StringBuilder(); for (int i = start; i < start + end; i++) { sb.append(byteToHex(bytes[i])); } return sb.toString(); } /** * * 将单个字节码转换成16进制字符串 * * @param bt 目标字节 * @return 转换结果 */ public static String byteToHex(byte bt) { return HEX_DIGITS[(bt & 0xf0) >> 4] + "" + HEX_DIGITS[bt & 0xf]; } /** * shiro密码加密工具类 * * @param credentials 密码 * @param salt 密码盐 * @return */ public static String md5(String credentials, String salt) { MessageDigest messageDigest = null; try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); //先加盐 messageDigest.update(salt.getBytes("UTF-8")); //再放需要被加密的数据 messageDigest.update(credentials.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { System.out.println("NoSuchAlgorithmException caught!"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } return md5StrBuff.toString(); } public static void main(String[] args) { System.out.println(MD5.md5("admin", "8pgby")); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/Maps.java ================================================ package cn.enilu.material.utils; import java.util.HashMap; /** * Map 工具类 * * @author enilu * */ public final class Maps { private Maps() { } public static HashMap newHashMap() { return new HashMap(); } public static HashMap newHashMap(K k, V v) { HashMap map = new HashMap(); map.put(k, v); return map; } @SuppressWarnings("unchecked") public static HashMap newHashMap(K k, V v, Object... extraKeyValues) { if (extraKeyValues.length % 2 != 0) { throw new IllegalArgumentException(); } HashMap map = new HashMap(); map.put(k, v); for (int i = 0; i < extraKeyValues.length; i += 2) { k = (K) extraKeyValues[i]; v = (V) extraKeyValues[i + 1]; map.put(k, v); } return map; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/MobileUtil.java ================================================ package cn.enilu.material.utils; import com.google.common.base.Strings; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MobileUtil { public static final Set prefixIds = new HashSet(); final static Pattern PHONE_START_WITH_ZERO = Pattern.compile("^01(3[0-9]|4[579]|5[0-35-9]|7[0135678]|8[0-9])[0-9]{8}$"); static { prefixIds.add("12590"); prefixIds.add("12593"); prefixIds.add("17901"); prefixIds.add("17911"); prefixIds.add("17951"); prefixIds.add("10193"); } public enum MobileType {ChinaTelecom, ChinaUnicom, ChinaMobile, UnKnow} final static String chinaTelecomRegex = "(133|153|180|181|189|177|173)\\d{8}|1700\\d{7}"; final static String chinaUnicomRegex = "(130|131|132|155|156|145|185|186|176)\\d{8}|1709\\d{7}"; final static String chinaMobileRegex = "(13[5-9]|15[0-2|7-9]|18[2-4|7-8]|147|178)\\d{8}|(134[0-8]|1705)\\d{7}"; public static final Pattern P_RULE_1 = Pattern.compile("^((10)|(2[1-9])).*"); public static final Pattern P_RULE_2 = Pattern.compile("^[3-9].*"); public static MobileType type(String mobile) { if (mobile.matches(chinaTelecomRegex)) { return MobileType.ChinaTelecom; } else if (mobile.matches(chinaUnicomRegex)) { return MobileType.ChinaUnicom; } else if (mobile.matches(chinaMobileRegex)) { return MobileType.ChinaMobile; } return MobileType.UnKnow; } public static String cleanPhone(String value) { value = strClean(value); if (value.startsWith("+86")) { value = value.substring(3); } if (value.length() == 13) { // 8613800210500 if (value.startsWith("86")) { value = value.substring(2); } } else if (value.length() >= 14 && value.length() <= 16) { // 008613637037976 if (value.startsWith("0086")) { value = value.substring(4); } } if (PHONE_START_WITH_ZERO.matcher(value).matches()) { value = value.substring(1); } // 如果号码是10位,且前2位是10,21,22,23,24,25,26,27,28,29,应该是固话,在第一位补上0 // 如果号码是10位,且第1位是3,4,5,6,7,8,9(非400、800开头),应该也是固话,在第一位补上0 // 如果号码是11位,且第1位是3,4,5,6,7,8,9,应该也是固话,在第一位补上0 if (value.length() == 10) { Matcher m = P_RULE_1.matcher(value); if (m.find()) { value = "0" + value; } else { m = P_RULE_2.matcher(value); if (m.find()) { if (!value.startsWith("400") && !value.startsWith("800")) { value = "0" + value; } } } } else if (value.length() == 11) { Matcher m = P_RULE_2.matcher(value); if (m.find()) { value = "0" + value; } } else if (value.length() == 12) { if (value.startsWith("01")) { value = value.substring(1); } } else if (value.length() == 16) { for (String id : prefixIds) { if (value.startsWith(id)) { value = value.substring(5); break; } } } return value; } public static String strClean(String value) { if (Strings.isNullOrEmpty(value)) { return ""; } // 去除首尾空白字符、全角空格等 return value.trim().replaceAll("[\\s\\u00A0\\u3000]+$", "").replaceAll("^[\\s\\u00A0\\u3000]+", ""); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/PageKit.java ================================================ package cn.enilu.material.utils; /** * 分页工具类 * * @author xiaoleilu * */ public class PageKit { /** * 将页数和每页条目数转换为开始位置和结束位置
    * 此方法用于不包括结束位置的分页方法
    * 例如:
    * 页码:1,每页10 -> [0, 10]
    * 页码:2,每页10 -> [10, 20]
    * 。。。
    * * @param pageNo * 页码(从1计数) * @param countPerPage * 每页条目数 * @return 第一个数为开始位置,第二个数为结束位置 */ public static int[] transToStartEnd(int pageNo, int countPerPage) { if (pageNo < 1) { pageNo = 1; } if (countPerPage < 1) { countPerPage = 0; // LogKit.warn("Count per page [" + countPerPage + "] is not valid!"); } int start = (pageNo - 1) * countPerPage; int end = start + countPerPage; return new int[] { start, end }; } /** * 根据总数计算总页数 * * @param totalCount * 总数 * @param numPerPage * 每页数 * @return 总页数 */ public static int totalPage(int totalCount, int numPerPage) { if (numPerPage == 0) { return 0; } return totalCount % numPerPage == 0 ? (totalCount / numPerPage) : (totalCount / numPerPage + 1); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/RSAUtil.java ================================================ package cn.enilu.material.utils; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; /** * 非对称加密算法RSA算法组件 * 非对称算法一般是用来传送对称加密算法的密钥来使用的,相对于DH算法,RSA算法只需要一方构造密钥,不需要 * 大费周章的构造各自本地的密钥对了。DH算法只能算法非对称算法的底层实现。而RSA算法算法实现起来较为简单 * * @author mayanyun */ public class RSAUtil { //非对称密钥算法 public static final String KEY_ALGORITHM = "RSA"; /** * 密钥长度,DH算法的默认密钥长度是1024 * 密钥长度必须是64的倍数,在512到65536位之间 */ private static final int KEY_SIZE = 512; //公钥 private static final String PUBLIC_KEY = "RSAPublicKey"; //私钥 private static final String PRIVATE_KEY = "RSAPrivateKey"; /** * 初始化密钥对 * * @return Map 甲方密钥的Map */ public static Map initKey() throws Exception { //实例化密钥生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); //初始化密钥生成器 keyPairGenerator.initialize(KEY_SIZE); //生成密钥对 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //甲方公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); //甲方私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); //将密钥存储在map中 Map keyMap = new HashMap(); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 私钥加密 * * @param data 待加密数据 * @param key 密钥 * @return byte[] 加密数据 */ public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception { //取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //生成私钥 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); //数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 公钥加密 * * @param data 待加密数据 * @param key 密钥 * @return byte[] 加密数据 */ public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception { //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //初始化公钥 //密钥材料转换 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, pubKey); return cipher.doFinal(data); } /** * 私钥解密 * * @param data 待解密数据 * @param key 密钥 * @return byte[] 解密数据 */ public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception { //取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //生成私钥 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); //数据解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 公钥解密 * * @param data 待解密数据 * @param key 密钥 * @return byte[] 解密数据 */ public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception { //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //初始化公钥 //密钥材料转换 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //数据解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, pubKey); return cipher.doFinal(data); } /** * 取得私钥 * * @param keyMap 密钥map * @return byte[] 私钥 */ public static byte[] getPrivateKey(Map keyMap) { Key key = (Key) keyMap.get(PRIVATE_KEY); return key.getEncoded(); } /** * 取得公钥 * * @param keyMap 密钥map * @return byte[] 公钥 */ public static byte[] getPublicKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return key.getEncoded(); } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //初始化密钥 //生成密钥对 Map keyMap = RSAUtil.initKey(); //公钥 byte[] publicKey = RSAUtil.getPublicKey(keyMap); //私钥 byte[] privateKey = RSAUtil.getPrivateKey(keyMap); System.out.println("公钥:/n" + Base64.encodeBase64String(publicKey)); System.out.println("私钥:/n" + Base64.encodeBase64String(privateKey)); System.out.println("================密钥对构造完毕,甲方将公钥公布给乙方,开始进行加密数据的传输============="); String str = "RSA密码交换算法"; System.out.println("/n===========甲方向乙方发送加密数据=============="); System.out.println("原文:" + str); //甲方进行数据的加密 byte[] code1 = RSAUtil.encryptByPrivateKey(str.getBytes(), privateKey); System.out.println("加密后的数据:" + Base64.encodeBase64String(code1)); System.out.println("===========乙方使用甲方提供的公钥对数据进行解密=============="); //乙方进行数据的解密 byte[] decode1 = RSAUtil.decryptByPublicKey(code1, publicKey); System.out.println("乙方解密后的数据:" + new String(decode1) + "/n/n"); System.out.println("===========反向进行操作,乙方向甲方发送数据==============/n/n"); str = "乙方向甲方发送数据RSA算法"; System.out.println("原文:" + str); //乙方使用公钥对数据进行加密 byte[] code2 = RSAUtil.encryptByPublicKey(str.getBytes(), publicKey); System.out.println("===========乙方使用公钥对数据进行加密=============="); System.out.println("加密后的数据:" + Base64.encodeBase64String(code2)); System.out.println("=============乙方将数据传送给甲方======================"); System.out.println("===========甲方使用私钥对数据进行解密=============="); //甲方使用私钥对数据进行解密 byte[] decode2 = RSAUtil.decryptByPrivateKey(code2, privateKey); System.out.println("甲方解密后的数据:" + new String(decode2)); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/RandomUtils.java ================================================ package cn.enilu.material.utils; import java.text.DecimalFormat; import java.util.*; /** * 随机数工具 */ public class RandomUtils { private static char[] alpha = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; private static Random ran; static { ran = new Random(); ran.setSeed(System.currentTimeMillis()); } public static char nextChar() { return alpha[Math.abs(ran.nextInt()) % 36]; } public static String nextChar(int i) { StringBuffer sb = new StringBuffer(); for (int j = 0; j < i; j++) { sb.append(nextChar()); } return sb.toString(); } /** * 产生普通的随机数 * fmt: #0.00 数值格式 */ public static String getNormalRandom(int range, String fmt) { DecimalFormat format = new DecimalFormat(fmt); double randomNum = Math.random() * 20; return format.format(randomNum); } /** * 产生指定位数的字符随机字符串 */ public static String getCharacterRandom(int length) { return getRandom("a-z,A-Z", length); } /** * 产生指定位数的数字随机字符串 */ public static String getNumberRandom(int length) { return getRandom("1-9", length); } /** * 产生指定范围和位数的随机字符串 * range:a-z,A-Z,0-9 */ public static String getRandom(String range, int length) { StringBuffer result = new StringBuffer(); List randomRange = new ArrayList(); try { if (range != null && range.trim().length() > 0) { String[] rangeGroup = range.split(","); for (String group : rangeGroup) { String[] charStr = group.split("-"); for (char i = charStr[0].charAt(0); i <= charStr[1].charAt(0); i++) { randomRange.add(i); } } } for (int i = 0; i < length; i++) { Object randomObj = randomRange.get(new Random().nextInt(randomRange.size())); result.append(randomObj); } } catch (Exception e) { e.printStackTrace(); } return result.toString(); } /** * 产生36位的UUID随机字符串 */ public static String getUUIDRandom() { return UUID.randomUUID().toString(); } /** * 产生可定制的时间戳随机字符串 */ public static String getTimestampRandom(String head, String tail, int tailBit) { return head + DateUtil.getAllTime() + tail + RandomUtils.getNumberRandom(tailBit); } public static String randomCode() { String[] beforeShuffle = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; List list = Arrays.asList(beforeShuffle); Collections.shuffle(list); StringBuilder sb = new StringBuilder(); for (int i = 0; i < list.size(); i++) { sb.append(list.get(i)); } String afterShuffle = sb.toString(); String result = afterShuffle.substring(5, 9); return result; } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/StrKit.java ================================================ package cn.enilu.material.utils; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * 字符串工具类 * * @author xiaoleilu * */ public class StrKit { public static final String SPACE = " "; public static final String DOT = "."; public static final String SLASH = "/"; public static final String BACKSLASH = "\\"; public static final String EMPTY = ""; public static final String CRLF = "\r\n"; public static final String NEWLINE = "\n"; public static final String UNDERLINE = "_"; public static final String COMMA = ","; public static final String HTML_NBSP = " "; public static final String HTML_AMP = "&"; public static final String HTML_QUOTE = """; public static final String HTML_LT = "<"; public static final String HTML_GT = ">"; public static final String EMPTY_JSON = "{}"; /** * 首字母变小写 */ public static String firstCharToLowerCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'A' && firstChar <= 'Z') { char[] arr = str.toCharArray(); arr[0] += ('a' - 'A'); return new String(arr); } return str; } /** * 首字母变大写 */ public static String firstCharToUpperCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'a' && firstChar <= 'z') { char[] arr = str.toCharArray(); arr[0] -= ('a' - 'A'); return new String(arr); } return str; } // ------------------------------------------------------------------------ Blank /** * 字符串是否为空白 空白的定义如下:
    * 1、为null
    * 2、为不可见字符(如空格)
    * 3、""
    * * @param str 被检测的字符串 * @return 是否为空 */ public static boolean isBlank(String str) { int length; if ((str == null) || ((length = str.length()) == 0)) { return true; } for (int i = 0; i < length; i++) { // 只要有一个非空字符即为非空字符串 if (false == Character.isWhitespace(str.charAt(i))) { return false; } } return true; } /** * 字符串是否为非空白 空白的定义如下:
    * 1、不为null
    * 2、不为不可见字符(如空格)
    * 3、不为""
    * * @param str 被检测的字符串 * @return 是否为非空 */ public static boolean notBlank(String str) { return false == isBlank(str); } /** * 是否包含空字符串 * * @param strs 字符串列表 * @return 是否包含空字符串 */ public static boolean hasBlank(String... strs) { if (CollectionKit.isEmpty(strs)) { return true; } for (String str : strs) { if (isBlank(str)) { return true; } } return false; } /** * 给定所有字符串是否为空白 * * @param strs 字符串 * @return 所有字符串是否为空白 */ public static boolean isAllBlank(String... strs) { if (CollectionKit.isEmpty(strs)) { return true; } for (String str : strs) { if (notBlank(str)) { return false; } } return true; } // ------------------------------------------------------------------------ Empty /** * 字符串是否为空,空的定义如下 1、为null
    * 2、为""
    * * @param str 被检测的字符串 * @return 是否为空 */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } /** * 字符串是否为非空白 空白的定义如下:
    * 1、不为null
    * 2、不为""
    * * @param str 被检测的字符串 * @return 是否为非空 */ public static boolean isNotEmpty(String str) { return false == isEmpty(str); } /** * 当给定字符串为null时,转换为Empty * * @param str 被转换的字符串 * @return 转换后的字符串 */ public static String nullToEmpty(String str) { return nullToDefault(str, EMPTY); } /** * 如果字符串是null,则返回指定默认字符串,否则返回字符串本身。 * *

    	 * nullToDefault(null, "default")  = "default"
    	 * nullToDefault("", "default")    = ""
    	 * nullToDefault("  ", "default")  = "  "
    	 * nullToDefault("bat", "default") = "bat"
    	 * 
    * * @param str 要转换的字符串 * @param defaultStr 默认字符串 * * @return 字符串本身或指定的默认字符串 */ public static String nullToDefault(String str, String defaultStr) { return (str == null) ? defaultStr : str; } /** * 当给定字符串为空字符串时,转换为null * * @param str 被转换的字符串 * @return 转换后的字符串 */ public static String emptyToNull(String str) { return isEmpty(str) ? null : str; } /** * 是否包含空字符串 * * @param strs 字符串列表 * @return 是否包含空字符串 */ public static boolean hasEmpty(String... strs) { if (CollectionKit.isEmpty(strs)) { return true; } for (String str : strs) { if (isEmpty(str)) { return true; } } return false; } /** * 是否全部为空字符串 * * @param strs 字符串列表 * @return 是否全部为空字符串 */ public static boolean isAllEmpty(String... strs) { if (CollectionKit.isEmpty(strs)) { return true; } for (String str : strs) { if (isNotEmpty(str)) { return false; } } return true; } // ------------------------------------------------------------------------ Trim /** * 除去字符串头尾部的空白,如果字符串是null,依然返回null。 * *

    * 注意,和String.trim不同,此方法使用Character.isWhitespace 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。 * *

    	 * trim(null)          = null
    	 * trim("")            = ""
    	 * trim("     ")       = ""
    	 * trim("abc")         = "abc"
    	 * trim("    abc    ") = "abc"
    	 * 
    * *

    * * @param str 要处理的字符串 * * @return 除去空白的字符串,如果原字串为null,则返回null */ public static String trim(String str) { return (null == str) ? null : trim(str, 0); } /** * 给定字符串数组全部做去首尾空格 * * @param strs 字符串数组 */ public static void trim(String[] strs) { if (null == strs) { return; } String str; for (int i = 0; i < strs.length; i++) { str = strs[i]; if (null != str) { strs[i] = str.trim(); } } } /** * 除去字符串头部的空白,如果字符串是null,则返回null。 * *

    * 注意,和String.trim不同,此方法使用Character.isWhitespace 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。 * *

    	 * trimStart(null)         = null
    	 * trimStart("")           = ""
    	 * trimStart("abc")        = "abc"
    	 * trimStart("  abc")      = "abc"
    	 * trimStart("abc  ")      = "abc  "
    	 * trimStart(" abc ")      = "abc "
    	 * 
    * *

    * * @param str 要处理的字符串 * * @return 除去空白的字符串,如果原字串为null或结果字符串为"",则返回 null */ public static String trimStart(String str) { return trim(str, -1); } /** * 除去字符串尾部的空白,如果字符串是null,则返回null。 * *

    * 注意,和String.trim不同,此方法使用Character.isWhitespace 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。 * *

    	 * trimEnd(null)       = null
    	 * trimEnd("")         = ""
    	 * trimEnd("abc")      = "abc"
    	 * trimEnd("  abc")    = "  abc"
    	 * trimEnd("abc  ")    = "abc"
    	 * trimEnd(" abc ")    = " abc"
    	 * 
    * *

    * * @param str 要处理的字符串 * * @return 除去空白的字符串,如果原字串为null或结果字符串为"",则返回 null */ public static String trimEnd(String str) { return trim(str, 1); } /** * 除去字符串头尾部的空白符,如果字符串是null,依然返回null。 * * @param str 要处理的字符串 * @param mode -1表示trimStart,0表示trim全部, 1表示trimEnd * * @return 除去指定字符后的的字符串,如果原字串为null,则返回null */ public static String trim(String str, int mode) { if (str == null) { return null; } int length = str.length(); int start = 0; int end = length; // 扫描字符串头部 if (mode <= 0) { while ((start < end) && (Character.isWhitespace(str.charAt(start)))) { start++; } } // 扫描字符串尾部 if (mode >= 0) { while ((start < end) && (Character.isWhitespace(str.charAt(end - 1)))) { end--; } } if ((start > 0) || (end < length)) { return str.substring(start, end); } return str; } /** * 是否以指定字符串开头 * @param str 被监测字符串 * @param prefix 开头字符串 * @param isIgnoreCase 是否忽略大小写 * @return 是否以指定字符串开头 */ public static boolean startWith(String str, String prefix, boolean isIgnoreCase){ if(isIgnoreCase){ return str.toLowerCase().startsWith(prefix.toLowerCase()); }else{ return str.startsWith(prefix); } } /** * 是否以指定字符串结尾 * @param str 被监测字符串 * @param suffix 结尾字符串 * @param isIgnoreCase 是否忽略大小写 * @return 是否以指定字符串结尾 */ public static boolean endWith(String str, String suffix, boolean isIgnoreCase){ if(isIgnoreCase){ return str.toLowerCase().endsWith(suffix.toLowerCase()); }else{ return str.endsWith(suffix); } } /** * 是否包含特定字符,忽略大小写,如果给定两个参数都为null,返回true * @param str 被检测字符串 * @param testStr 被测试是否包含的字符串 * @return 是否包含 */ public static boolean containsIgnoreCase(String str, String testStr){ if(null == str){ //如果被监测字符串和 return null == testStr; } return str.toLowerCase().contains(testStr.toLowerCase()); } /** * 获得set或get方法对应的标准属性名
    * 例如:setName 返回 name * * @param getOrSetMethodName * @return 如果是set或get方法名,返回field, 否则null */ public static String getGeneralField(String getOrSetMethodName) { if (getOrSetMethodName.startsWith("get") || getOrSetMethodName.startsWith("set")) { return cutPreAndLowerFirst(getOrSetMethodName, 3); } return null; } /** * 生成set方法名
    * 例如:name 返回 setName * * @param fieldName 属性名 * @return setXxx */ public static String genSetter(String fieldName) { return upperFirstAndAddPre(fieldName, "set"); } /** * 生成get方法名 * * @param fieldName 属性名 * @return getXxx */ public static String genGetter(String fieldName) { return upperFirstAndAddPre(fieldName, "get"); } /** * 去掉首部指定长度的字符串并将剩余字符串首字母小写
    * 例如:str=setName, preLength=3 -> return name * * @param str 被处理的字符串 * @param preLength 去掉的长度 * @return 处理后的字符串,不符合规范返回null */ public static String cutPreAndLowerFirst(String str, int preLength) { if (str == null) { return null; } if (str.length() > preLength) { char first = Character.toLowerCase(str.charAt(preLength)); if (str.length() > preLength + 1) { return first + str.substring(preLength + 1); } return String.valueOf(first); } return null; } /** * 原字符串首字母大写并在其首部添加指定字符串 例如:str=name, preString=get -> return getName * * @param str 被处理的字符串 * @param preString 添加的首部 * @return 处理后的字符串 */ public static String upperFirstAndAddPre(String str, String preString) { if (str == null || preString == null) { return null; } return preString + upperFirst(str); } /** * 大写首字母
    * 例如:str = name, return Name * * @param str 字符串 * @return 字符串 */ public static String upperFirst(String str) { return Character.toUpperCase(str.charAt(0)) + str.substring(1); } /** * 小写首字母
    * 例如:str = Name, return name * * @param str 字符串 * @return 字符串 */ public static String lowerFirst(String str) { if(isBlank(str)){ return str; } return Character.toLowerCase(str.charAt(0)) + str.substring(1); } /** * 去掉指定前缀 * * @param str 字符串 * @param prefix 前缀 * @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串 */ public static String removePrefix(String str, String prefix) { if(isEmpty(str) || isEmpty(prefix)){ return str; } if (str.startsWith(prefix)) { return str.substring(prefix.length()); } return str; } /** * 忽略大小写去掉指定前缀 * * @param str 字符串 * @param prefix 前缀 * @return 切掉后的字符串,若前缀不是 prefix, 返回原字符串 */ public static String removePrefixIgnoreCase(String str, String prefix) { if(isEmpty(str) || isEmpty(prefix)){ return str; } if (str.toLowerCase().startsWith(prefix.toLowerCase())) { return str.substring(prefix.length()); } return str; } /** * 去掉指定后缀 * * @param str 字符串 * @param suffix 后缀 * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串 */ public static String removeSuffix(String str, String suffix) { if(isEmpty(str) || isEmpty(suffix)){ return str; } if (str.endsWith(suffix)) { return str.substring(0, str.length() - suffix.length()); } return str; } /** * 获得字符串对应byte数组 * @param str 字符串 * @param charset 编码,如果为null使用系统默认编码 * @return bytes */ public static byte[] getBytes(String str, Charset charset){ if(null == str){ return null; } return null == charset ? str.getBytes() : str.getBytes(charset); } /** * 忽略大小写去掉指定后缀 * * @param str 字符串 * @param suffix 后缀 * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串 */ public static String removeSuffixIgnoreCase(String str, String suffix) { if(isEmpty(str) || isEmpty(suffix)){ return str; } if (str.toLowerCase().endsWith(suffix.toLowerCase())) { return str.substring(0, str.length() - suffix.length()); } return str; } /** * 如果给定字符串不是以prefix开头的,在开头补充 prefix * @param str 字符串 * @param prefix 前缀 * @return 补充后的字符串 */ public static String addPrefixIfNot(String str, String prefix){ if(isEmpty(str) || isEmpty(prefix)){ return str; } if(false == str.startsWith(prefix)){ str = prefix + str; } return str; } /** * 如果给定字符串不是以suffix结尾的,在尾部补充 suffix * @param str 字符串 * @param suffix 后缀 * @return 补充后的字符串 */ public static String addSuffixIfNot(String str, String suffix){ if(isEmpty(str) || isEmpty(suffix)){ return str; } if(false == str.endsWith(suffix)){ str += suffix; } return str; } /** * 清理空白字符 * * @param str 被清理的字符串 * @return 清理后的字符串 */ public static String cleanBlank(String str) { if (str == null) { return null; } return str.replaceAll("\\s*", EMPTY); } /** * 切分字符串
    * a#b#c -> [a,b,c]
    * a##b#c -> [a,"",b,c] * * @param str 被切分的字符串 * @param separator 分隔符字符 * @return 切分后的集合 */ public static List split(String str, char separator) { return split(str, separator, 0); } /** * 切分字符串 * * @param str 被切分的字符串 * @param separator 分隔符字符 * @param limit 限制分片数 * @return 切分后的集合 */ public static List split(String str, char separator, int limit) { if (str == null) { return null; } List list = new ArrayList(limit == 0 ? 16 : limit); if (limit == 1) { list.add(str); return list; } boolean isNotEnd = true; // 未结束切分的标志 int strLen = str.length(); StringBuilder sb = new StringBuilder(strLen); for (int i = 0; i < strLen; i++) { char c = str.charAt(i); if (isNotEnd && c == separator) { list.add(sb.toString()); // 清空StringBuilder sb.delete(0, sb.length()); // 当达到切分上限-1的量时,将所剩字符全部作为最后一个串 if (limit != 0 && list.size() == limit - 1) { isNotEnd = false; } } else { sb.append(c); } } list.add(sb.toString());// 加入尾串 return list; } /** * 切分字符串
    * from jodd * * @param str 被切分的字符串 * @param delimiter 分隔符 * @return 字符串 */ public static String[] split(String str, String delimiter) { if (str == null) { return null; } if (str.trim().length() == 0) { return new String[] { str }; } int dellen = delimiter.length(); // del length int maxparts = (str.length() / dellen) + 2; // one more for the last int[] positions = new int[maxparts]; int i, j = 0; int count = 0; positions[0] = -dellen; while ((i = str.indexOf(delimiter, j)) != -1) { count++; positions[count] = i; j = i + dellen; } count++; positions[count] = str.length(); String[] result = new String[count]; for (i = 0; i < count; i++) { result[i] = str.substring(positions[i] + dellen, positions[i + 1]); } return result; } /** * 改进JDK subString
    * index从0开始计算,最后一个字符为-1
    * 如果from和to位置一样,返回 ""
    * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
    * 如果经过修正的index中from大于to,则互换from和to * example:
    * abcdefgh 2 3 -> c
    * abcdefgh 2 -3 -> cde
    * * @param string String * @param fromIndex 开始的index(包括) * @param toIndex 结束的index(不包括) * @return 字串 */ public static String sub(String string, int fromIndex, int toIndex) { int len = string.length(); if (fromIndex < 0) { fromIndex = len + fromIndex; if(fromIndex < 0 ) { fromIndex = 0; } } else if(fromIndex >= len) { fromIndex = len -1; } if (toIndex < 0) { toIndex = len + toIndex; if(toIndex < 0) { toIndex = len; } } else if(toIndex > len) { toIndex = len; } if (toIndex < fromIndex) { int tmp = fromIndex; fromIndex = toIndex; toIndex = tmp; } if (fromIndex == toIndex) { return EMPTY; } char[] strArray = string.toCharArray(); char[] newStrArray = Arrays.copyOfRange(strArray, fromIndex, toIndex); return new String(newStrArray); } /** * 切割前部分 * * @param string 字符串 * @param toIndex 切割到的位置(不包括) * @return 切割后的字符串 */ public static String subPre(String string, int toIndex) { return sub(string, 0, toIndex); } /** * 切割后部分 * * @param string 字符串 * @param fromIndex 切割开始的位置(包括) * @return 切割后的字符串 */ public static String subSuf(String string, int fromIndex) { if (isEmpty(string)) { return null; } return sub(string, fromIndex, string.length()); } /** * 给定字符串是否被字符包围 * * @param str 字符串 * @param prefix 前缀 * @param suffix 后缀 * @return 是否包围,空串不包围 */ public static boolean isSurround(String str, String prefix, String suffix) { if (StrKit.isBlank(str)) { return false; } if (str.length() < (prefix.length() + suffix.length())) { return false; } return str.startsWith(prefix) && str.endsWith(suffix); } /** * 给定字符串是否被字符包围 * * @param str 字符串 * @param prefix 前缀 * @param suffix 后缀 * @return 是否包围,空串不包围 */ public static boolean isSurround(String str, char prefix, char suffix) { if (StrKit.isBlank(str)) { return false; } if (str.length() < 2) { return false; } return str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix; } /** * 重复某个字符 * * @param c 被重复的字符 * @param count 重复的数目 * @return 重复字符字符串 */ public static String repeat(char c, int count) { char[] result = new char[count]; for (int i = 0; i < count; i++) { result[i] = c; } return new String(result); } /** * 重复某个字符串 * * @param str 被重复的字符 * @param count 重复的数目 * @return 重复字符字符串 */ public static String repeat(String str, int count) { // 检查 final int len = str.length(); final long longSize = (long) len * (long) count; final int size = (int) longSize; if (size != longSize) { throw new ArrayIndexOutOfBoundsException("Required String length is too large: " + longSize); } final char[] array = new char[size]; str.getChars(0, len, array, 0); int n; for (n = len; n < size - n; n <<= 1) {// n <<= 1相当于n *2 System.arraycopy(array, 0, array, n, n); } System.arraycopy(array, 0, array, n, size - n); return new String(array); } /** * 比较两个字符串(大小写敏感)。 * *
    	 * equals(null, null)   = true
    	 * equals(null, "abc")  = false
    	 * equals("abc", null)  = false
    	 * equals("abc", "abc") = true
    	 * equals("abc", "ABC") = false
    	 * 
    * * @param str1 要比较的字符串1 * @param str2 要比较的字符串2 * * @return 如果两个字符串相同,或者都是null,则返回true */ public static boolean equals(String str1, String str2) { if (str1 == null) { return str2 == null; } return str1.equals(str2); } /** * 比较两个字符串(大小写不敏感)。 * *
    	 * equalsIgnoreCase(null, null)   = true
    	 * equalsIgnoreCase(null, "abc")  = false
    	 * equalsIgnoreCase("abc", null)  = false
    	 * equalsIgnoreCase("abc", "abc") = true
    	 * equalsIgnoreCase("abc", "ABC") = true
    	 * 
    * * @param str1 要比较的字符串1 * @param str2 要比较的字符串2 * * @return 如果两个字符串相同,或者都是null,则返回true */ public static boolean equalsIgnoreCase(String str1, String str2) { if (str1 == null) { return str2 == null; } return str1.equalsIgnoreCase(str2); } /** * 格式化文本, {} 表示占位符
    * 例如:format("aaa {} ccc", "bbb") ----> aaa bbb ccc * * @param template 文本模板,被替换的部分用 {} 表示 * @param values 参数值 * @return 格式化后的文本 */ public static String format(String template, Object... values) { if (CollectionKit.isEmpty(values) || isBlank(template)) { return template; } final StringBuilder sb = new StringBuilder(); final int length = template.length(); int valueIndex = 0; char currentChar; for (int i = 0; i < length; i++) { if (valueIndex >= values.length) { sb.append(sub(template, i, length)); break; } currentChar = template.charAt(i); if (currentChar == '{') { final char nextChar = template.charAt(++i); if (nextChar == '}') { sb.append(values[valueIndex++]); } else { sb.append('{').append(nextChar); } } else { sb.append(currentChar); } } return sb.toString(); } /** * 格式化文本,使用 {varName} 占位
    * map = {a: "aValue", b: "bValue"} * format("{a} and {b}", map) ----> aValue and bValue * * @param template 文本模板,被替换的部分用 {key} 表示 * @param map 参数值对 * @return 格式化后的文本 */ public static String format(String template, Map map) { if (null == map || map.isEmpty()) { return template; } for (Entry entry : map.entrySet()) { template = template.replace("{" + entry.getKey() + "}", entry.getValue().toString()); } return template; } /** * 编码字符串 * * @param str 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 编码后的字节码 */ public static byte[] bytes(String str, String charset) { return bytes(str, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset)); } /** * 编码字符串 * * @param str 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 编码后的字节码 */ public static byte[] bytes(String str, Charset charset) { if (str == null) { return null; } if (null == charset) { return str.getBytes(); } return str.getBytes(charset); } /** * 将byte数组转为字符串 * * @param bytes byte数组 * @param charset 字符集 * @return 字符串 */ public static String str(byte[] bytes, String charset) { return str(bytes, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset)); } /** * 解码字节码 * * @param data 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 解码后的字符串 */ public static String str(byte[] data, Charset charset) { if (data == null) { return null; } if (null == charset) { return new String(data); } return new String(data, charset); } /** * 将编码的byteBuffer数据转换为字符串 * @param data 数据 * @param charset 字符集,如果为空使用当前系统字符集 * @return 字符串 */ public static String str(ByteBuffer data, String charset){ if(data == null) { return null; } return str(data, Charset.forName(charset)); } /** * 将编码的byteBuffer数据转换为字符串 * @param data 数据 * @param charset 字符集,如果为空使用当前系统字符集 * @return 字符串 */ public static String str(ByteBuffer data, Charset charset){ if(null == charset) { charset = Charset.defaultCharset(); } return charset.decode(data).toString(); } /** * 字符串转换为byteBuffer * @param str 字符串 * @param charset 编码 * @return byteBuffer */ public static ByteBuffer byteBuffer(String str, String charset) { return ByteBuffer.wrap(StrKit.bytes(str, charset)); } /** * 以 conjunction 为分隔符将多个对象转换为字符串 * * @param conjunction 分隔符 * @param objs 数组 * @return 连接后的字符串 */ public static String join(String conjunction, Object... objs) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (Object item : objs) { if (isFirst) { isFirst = false; } else { sb.append(conjunction); } sb.append(item); } return sb.toString(); } /** * 将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
    * 例如:HelloWorld->hello_world * * @param camelCaseStr 转换前的驼峰式命名的字符串 * @return 转换后下划线大写方式命名的字符串 */ public static String toUnderlineCase(String camelCaseStr) { if (camelCaseStr == null) { return null; } final int length = camelCaseStr.length(); StringBuilder sb = new StringBuilder(); char c; boolean isPreUpperCase = false; for (int i = 0; i < length; i++) { c = camelCaseStr.charAt(i); boolean isNextUpperCase = true; if (i < (length - 1)) { isNextUpperCase = Character.isUpperCase(camelCaseStr.charAt(i + 1)); } if (Character.isUpperCase(c)) { if (!isPreUpperCase || !isNextUpperCase) { if (i > 0) sb.append(UNDERLINE); } isPreUpperCase = true; } else { isPreUpperCase = false; } sb.append(Character.toLowerCase(c)); } return sb.toString(); } /** * 将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
    * 例如:hello_world->HelloWorld * * @param name 转换前的下划线大写方式命名的字符串 * @return 转换后的驼峰式命名的字符串 */ public static String toCamelCase(String name) { if (name == null) { return null; } if (name.contains(UNDERLINE)) { name = name.toLowerCase(); StringBuilder sb = new StringBuilder(name.length()); boolean upperCase = false; for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if (c == '_') { upperCase = true; } else if (upperCase) { sb.append(Character.toUpperCase(c)); upperCase = false; } else { sb.append(c); } } return sb.toString(); } else return name; } /** * 包装指定字符串 * * @param str 被包装的字符串 * @param prefix 前缀 * @param suffix 后缀 * @return 包装后的字符串 */ public static String wrap(String str, String prefix, String suffix) { return format("{}{}{}", prefix, str, suffix); } /** * 指定字符串是否被包装 * * @param str 字符串 * @param prefix 前缀 * @param suffix 后缀 * @return 是否被包装 */ public static boolean isWrap(String str, String prefix, String suffix) { return str.startsWith(prefix) && str.endsWith(suffix); } /** * 指定字符串是否被同一字符包装(前后都有这些字符串) * * @param str 字符串 * @param wrapper 包装字符串 * @return 是否被包装 */ public static boolean isWrap(String str, String wrapper) { return isWrap(str, wrapper, wrapper); } /** * 指定字符串是否被同一字符包装(前后都有这些字符串) * * @param str 字符串 * @param wrapper 包装字符 * @return 是否被包装 */ public static boolean isWrap(String str, char wrapper) { return isWrap(str, wrapper, wrapper); } /** * 指定字符串是否被包装 * * @param str 字符串 * @param prefixChar 前缀 * @param suffixChar 后缀 * @return 是否被包装 */ public static boolean isWrap(String str, char prefixChar, char suffixChar) { return str.charAt(0) == prefixChar && str.charAt(str.length() - 1) == suffixChar; } /** * 补充字符串以满足最小长度 StrUtil.padPre("1", 3, '0');//"001" * * @param str 字符串 * @param minLength 最小长度 * @param padChar 补充的字符 * @return 补充后的字符串 */ public static String padPre(String str, int minLength, char padChar) { if (str.length() >= minLength) { return str; } StringBuilder sb = new StringBuilder(minLength); for (int i = str.length(); i < minLength; i++) { sb.append(padChar); } sb.append(str); return sb.toString(); } /** * 补充字符串以满足最小长度 StrUtil.padEnd("1", 3, '0');//"100" * * @param str 字符串 * @param minLength 最小长度 * @param padChar 补充的字符 * @return 补充后的字符串 */ public static String padEnd(String str, int minLength, char padChar) { if (str.length() >= minLength) { return str; } StringBuilder sb = new StringBuilder(minLength); sb.append(str); for (int i = str.length(); i < minLength; i++) { sb.append(padChar); } return sb.toString(); } /** * 创建StringBuilder对象 * * @return StringBuilder对象 */ public static StringBuilder builder() { return new StringBuilder(); } /** * 创建StringBuilder对象 * * @return StringBuilder对象 */ public static StringBuilder builder(int capacity) { return new StringBuilder(capacity); } /** * 创建StringBuilder对象 * * @return StringBuilder对象 */ public static StringBuilder builder(String... strs) { final StringBuilder sb = new StringBuilder(); for (String str : strs) { sb.append(str); } return sb; } /** * 获得StringReader * * @param str 字符串 * @return StringReader */ public static StringReader getReader(String str) { return new StringReader(str); } /** * 获得StringWriter * * @return StringWriter */ public static StringWriter getWriter() { return new StringWriter(); } /** * 编码字符串 * * @param str 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 编码后的字节码 */ public static byte[] encode(String str, String charset) { if (str == null) { return null; } if(isBlank(charset)) { return str.getBytes(); } try { return str.getBytes(charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException(format("Charset [{}] unsupported!", charset)); } } /** * 解码字节码 * * @param data 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 解码后的字符串 */ public static String decode(byte[] data, String charset) { if (data == null) { return null; } if(isBlank(charset)) { return new String(data); } try { return new String(data, charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException(format("Charset [{}] unsupported!", charset)); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/StringUtils.java ================================================ package cn.enilu.material.utils; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; public class StringUtils { public static final String EMPTY = ""; private static final AtomicLong ORDER_SEQ = new AtomicLong(1); private static final Pattern PATERN_IP = Pattern.compile("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)"); /** * 是否为空字符 */ public static boolean isEmpty(String str) { if (str == null || str.trim().length() == 0) { return true; } if ("null".equalsIgnoreCase(str) || "undefined".equalsIgnoreCase(str)) { return true; } return false; } /** * 是否包含空字符串 * * @param strs 字符串列表 * @return 是否包含空字符串 */ public static boolean hasBlank(String... strs) { if (CollectionKit.isEmpty(strs)) { return true; } for (String str : strs) { if (isEmpty(str)) { return true; } } return false; } /** * 是否为非空字符 */ public static boolean isNotEmpty(String str) { return (!isEmpty(str)); } /** * 判断是否为null或空字符 */ public static boolean isNullOrEmpty(Object o) { if (o == null) { return true; } if (String.valueOf(o).replace((char) 12288, ' ').trim().length() == 0) { return true; } if ("null".equals(o)) { return true; } return false; } /** * 判断是否不为null或非空字符 */ public static boolean isNotNullOrEmpty(Object o) { return !isNullOrEmpty(o); } // 根据Unicode编码完美的判断中文汉字和符号 private static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) { return true; } return false; } public static String sNull(Object obj) { return obj == null ? "" : obj.toString(); } /** * 格式化文本, {} 表示占位符
    * 例如:format("aaa {} ccc", "bbb") ----> aaa bbb ccc * * @param template 文本模板,被替换的部分用 {} 表示 * @param values 参数值 * @return 格式化后的文本 */ public static String format(String template, Object... values) { if (CollectionKit.isEmpty(values) || isEmpty(template)) { return template; } final StringBuilder sb = new StringBuilder(); final int length = template.length(); int valueIndex = 0; char currentChar; for (int i = 0; i < length; i++) { if (valueIndex >= values.length) { sb.append(sub(template, i, length)); break; } currentChar = template.charAt(i); if (currentChar == '{') { final char nextChar = template.charAt(++i); if (nextChar == '}') { sb.append(values[valueIndex++]); } else { sb.append('{').append(nextChar); } } else { sb.append(currentChar); } } return sb.toString(); } /** * 格式化文本,使用 {varName} 占位
    * map = {a: "aValue", b: "bValue"} * format("{a} and {b}", map) ----> aValue and bValue * * @param template 文本模板,被替换的部分用 {key} 表示 * @param map 参数值对 * @return 格式化后的文本 */ public static String format(String template, Map map) { if (null == map || map.isEmpty()) { return template; } for (Map.Entry entry : map.entrySet()) { template = template.replace("{" + entry.getKey() + "}", entry.getValue().toString()); } return template; } /** * 改进JDK subString
    * index从0开始计算,最后一个字符为-1
    * 如果from和to位置一样,返回 ""
    * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
    * 如果经过修正的index中from大于to,则互换from和to * example:
    * abcdefgh 2 3 -> c
    * abcdefgh 2 -3 -> cde
    * * @param string String * @param fromIndex 开始的index(包括) * @param toIndex 结束的index(不包括) * @return 字串 */ public static String sub(String string, int fromIndex, int toIndex) { int len = string.length(); if (fromIndex < 0) { fromIndex = len + fromIndex; if (fromIndex < 0) { fromIndex = 0; } } else if (fromIndex >= len) { fromIndex = len - 1; } if (toIndex < 0) { toIndex = len + toIndex; if (toIndex < 0) { toIndex = len; } } else if (toIndex > len) { toIndex = len; } if (toIndex < fromIndex) { int tmp = fromIndex; fromIndex = toIndex; toIndex = tmp; } if (fromIndex == toIndex) { return EMPTY; } char[] strArray = string.toCharArray(); char[] newStrArray = Arrays.copyOfRange(strArray, fromIndex, toIndex); return new String(newStrArray); } /** * 首字母变小写 */ public static String firstCharToLowerCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'A' && firstChar <= 'Z') { char[] arr = str.toCharArray(); arr[0] += ('a' - 'A'); return new String(arr); } return str; } /** * 首字母变大写 */ public static String firstCharToUpperCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'a' && firstChar <= 'z') { char[] arr = str.toCharArray(); arr[0] -= ('a' - 'A'); return new String(arr); } return str; } /** * 去掉指定前缀 * * @param str 字符串 * @param prefix 前缀 * @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串 */ public static String removePrefix(String str, String prefix) { if (isEmpty(str) || isEmpty(prefix)) { return str; } if (str.startsWith(prefix)) { return str.substring(prefix.length()); } return str; } /** * 去掉指定后缀 * * @param str 字符串 * @param suffix 后缀 * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串 */ public static String removeSuffix(String str, String suffix) { if (isEmpty(str) || isEmpty(suffix)) { return str; } if (str.endsWith(suffix)) { return str.substring(0, str.length() - suffix.length()); } return str; } /** * 获得字符串对应byte数组 * * @param str 字符串 * @param charset 编码,如果为null使用系统默认编码 * @return bytes */ public static byte[] getBytes(String str, Charset charset) { if (null == str) { return null; } return null == charset ? str.getBytes() : str.getBytes(charset); } /** * 切分字符串
    * from jodd * * @param str 被切分的字符串 * @param delimiter 分隔符 * @return 字符串 */ public static String[] split(String str, String delimiter) { if (str == null) { return null; } if (str.trim().length() == 0) { return new String[]{str}; } int dellen = delimiter.length(); // del length int maxparts = (str.length() / dellen) + 2; // one more for the last int[] positions = new int[maxparts]; int i, j = 0; int count = 0; positions[0] = -dellen; while ((i = str.indexOf(delimiter, j)) != -1) { count++; positions[count] = i; j = i + dellen; } count++; positions[count] = str.length(); String[] result = new String[count]; for (i = 0; i < count; i++) { result[i] = str.substring(positions[i] + dellen, positions[i + 1]); } return result; } /** * 比较两个字符串(大小写敏感)。 * *
         * equals(null, null)   = true
         * equals(null, "abc")  = false
         * equals("abc", null)  = false
         * equals("abc", "abc") = true
         * equals("abc", "ABC") = false
         * 
    * * @param str1 要比较的字符串1 * @param str2 要比较的字符串2 * @return 如果两个字符串相同,或者都是null,则返回true */ public static boolean equals(String str1, String str2) { if (str1 == null) { return str2 == null; } return str1.equals(str2); } /** * 编码字符串 * * @param str 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 编码后的字节码 */ public static byte[] bytes(String str, Charset charset) { if (str == null) { return null; } if (null == charset) { return str.getBytes(); } return str.getBytes(charset); } /** * 解码字节码 * * @param data 字符串 * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 * @return 解码后的字符串 */ public static String str(byte[] data, Charset charset) { if (data == null) { return null; } if (null == charset) { return new String(data); } return new String(data, charset); } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/ToolUtil.java ================================================ /** * Copyright (c) 2015-2016, Chill Zhuang 庄骞 (smallchill@163.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.utils; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Array; import java.math.BigDecimal; import java.net.URISyntaxException; import java.util.*; import java.util.Map.Entry; /** * 高频方法集合类 */ public class ToolUtil { /** * 获取随机位数的字符串 * * @author fengshuonan * @Date 2017/8/24 14:09 */ public static String getRandomString(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } /** * 判断一个对象是否是时间类型 * * @author enilu.cn * @Date 2017/4/18 12:55 */ public static String dateType(Object o) { if (o instanceof Date) { return DateUtil.getDay((Date) o); } else { return o.toString(); } } /** * 获取异常的具体信息 * * @author fengshuonan * @Date 2017/3/30 9:21 * @version 2.0 */ public static String getExceptionMsg(Exception e) { StringWriter sw = new StringWriter(); try { e.printStackTrace(new PrintWriter(sw)); } finally { try { sw.close(); } catch (IOException e1) { e1.printStackTrace(); } } return sw.getBuffer().toString().replaceAll("\\$", "T"); } /** * 比较两个对象是否相等。
    * 相同的条件有两个,满足其一即可:
    * 1. obj1 == null && obj2 == null; 2. obj1.equals(obj2) * * @param obj1 对象1 * @param obj2 对象2 * @return 是否相等 */ public static boolean equals(Object obj1, Object obj2) { return (obj1 != null) ? (obj1.equals(obj2)) : (obj2 == null); } /** * 计算对象长度,如果是字符串调用其length函数,集合类调用其size函数,数组调用其length属性,其他可遍历对象遍历计算长度 * * @param obj 被计算长度的对象 * @return 长度 */ public static int length(Object obj) { if (obj == null) { return 0; } if (obj instanceof CharSequence) { return ((CharSequence) obj).length(); } if (obj instanceof Collection) { return ((Collection) obj).size(); } if (obj instanceof Map) { return ((Map) obj).size(); } int count; if (obj instanceof Iterator) { Iterator iter = (Iterator) obj; count = 0; while (iter.hasNext()) { count++; iter.next(); } return count; } if (obj instanceof Enumeration) { Enumeration enumeration = (Enumeration) obj; count = 0; while (enumeration.hasMoreElements()) { count++; enumeration.nextElement(); } return count; } if (obj.getClass().isArray() == true) { return Array.getLength(obj); } return -1; } /** * 对象中是否包含元素 * * @param obj 对象 * @param element 元素 * @return 是否包含 */ public static boolean contains(Object obj, Object element) { if (obj == null) { return false; } if (obj instanceof String) { if (element == null) { return false; } return ((String) obj).contains(element.toString()); } if (obj instanceof Collection) { return ((Collection) obj).contains(element); } if (obj instanceof Map) { return ((Map) obj).values().contains(element); } if (obj instanceof Iterator) { Iterator iter = (Iterator) obj; while (iter.hasNext()) { Object o = iter.next(); if (equals(o, element)) { return true; } } return false; } if (obj instanceof Enumeration) { Enumeration enumeration = (Enumeration) obj; while (enumeration.hasMoreElements()) { Object o = enumeration.nextElement(); if (equals(o, element)) { return true; } } return false; } if (obj.getClass().isArray() == true) { int len = Array.getLength(obj); for (int i = 0; i < len; i++) { Object o = Array.get(obj, i); if (equals(o, element)) { return true; } } } return false; } /** * 对象是否不为空(新增) * * @param obj String,List,Map,Object[],int[],long[] * @return */ public static boolean isNotEmpty(Object o) { return !isEmpty(o); } /** * 对象是否为空 * * @param obj String,List,Map,Object[],int[],long[] * @return */ @SuppressWarnings("rawtypes") public static boolean isEmpty(Object o) { if (o == null) { return true; } if (o instanceof String) { if (o.toString().trim().equals("")) { return true; } } else if (o instanceof List) { if (((List) o).size() == 0) { return true; } } else if (o instanceof Map) { if (((Map) o).size() == 0) { return true; } } else if (o instanceof Set) { if (((Set) o).size() == 0) { return true; } } else if (o instanceof Object[]) { if (((Object[]) o).length == 0) { return true; } } else if (o instanceof int[]) { if (((int[]) o).length == 0) { return true; } } else if (o instanceof long[]) { if (((long[]) o).length == 0) { return true; } } return false; } /** * 对象组中是否存在 Empty Object * * @param os 对象组 * @return */ public static boolean isOneEmpty(Object... os) { for (Object o : os) { if (isEmpty(o)) { return true; } } return false; } /** * 对象组中是否全是 Empty Object * * @param os * @return */ public static boolean isAllEmpty(Object... os) { for (Object o : os) { if (!isEmpty(o)) { return false; } } return true; } /** * 是否为数字 * * @param obj * @return */ public static boolean isNum(Object obj) { try { Integer.parseInt(obj.toString()); } catch (Exception e) { return false; } return true; } /** * 如果为空, 则调用默认值 * * @param str * @return */ public static Object getValue(Object str, Object defaultValue) { if (isEmpty(str)) { return defaultValue; } return str; } /** * 格式化文本 * * @param template 文本模板,被替换的部分用 {} 表示 * @param values 参数值 * @return 格式化后的文本 */ public static String format(String template, Object... values) { return StrKit.format(template, values); } /** * 格式化文本 * * @param template 文本模板,被替换的部分用 {key} 表示 * @param map 参数值对 * @return 格式化后的文本 */ public static String format(String template, Map map) { return StrKit.format(template, map); } /** * 强转->string,并去掉多余空格 * * @param str * @return */ public static String toStr(Object str) { return toStr(str, ""); } /** * 强转->string,并去掉多余空格 * * @param str * @param defaultValue * @return */ public static String toStr(Object str, String defaultValue) { if (null == str) { return defaultValue; } return str.toString().trim(); } /** * 强转->int * * @param obj * @return */ // public static int toInt(Object value) { // return toInt(value, -1); // } /** * 强转->int * * @param obj * @param defaultValue * @return */ // public static int toInt(Object value, int defaultValue) { // return Convert.toInt(value, defaultValue); // } /** * 强转->long * * @param obj * @return */ // public static long toLong(Object value) { // return toLong(value, -1); // } /** * 强转->long * * @param obj * @param defaultValue * @return */ // public static long toLong(Object value, long defaultValue) { // return Convert.toLong(value, defaultValue); // } // // public static String encodeUrl(String url) { // return URLKit.encode(url, CharsetKit.UTF_8); // } // // public static String decodeUrl(String url) { // return URLKit.decode(url, CharsetKit.UTF_8); // } /** * map的key转为小写 * * @param map * @return Map */ public static Map caseInsensitiveMap(Map map) { Map tempMap = new HashMap<>(); for (String key : map.keySet()) { tempMap.put(key.toLowerCase(), map.get(key)); } return tempMap; } /** * 获取map中第一个数据值 * * @param Key的类型 * @param Value的类型 * @param map 数据源 * @return 返回的值 */ public static V getFirstOrNull(Map map) { V obj = null; for (Entry entry : map.entrySet()) { obj = entry.getValue(); if (obj != null) { break; } } return obj; } /** * 创建StringBuilder对象 * * @return StringBuilder对象 */ public static StringBuilder builder(String... strs) { final StringBuilder sb = new StringBuilder(); for (String str : strs) { sb.append(str); } return sb; } /** * 创建StringBuilder对象 * * @return StringBuilder对象 */ public static void builder(StringBuilder sb, String... strs) { for (String str : strs) { sb.append(str); } } /** * 去掉指定后缀 * * @param str 字符串 * @param suffix 后缀 * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串 */ public static String removeSuffix(String str, String suffix) { if (isEmpty(str) || isEmpty(suffix)) { return str; } if (str.endsWith(suffix)) { return str.substring(0, str.length() - suffix.length()); } return str; } /** * 当前时间 * * @author enilu.cn * @Date 2017/5/7 21:56 */ public static String currentTime() { return DateUtil.getTime(); } /** * 首字母大写 * * @author enilu.cn * @Date 2017/5/7 22:01 */ public static String firstLetterToUpper(String val) { return StrKit.firstCharToUpperCase(val); } /** * 首字母小写 * * @author enilu.cn * @Date 2017/5/7 22:02 */ public static String firstLetterToLower(String val) { return StrKit.firstCharToLowerCase(val); } /** * 判断是否是windows操作系统 * * @author enilu.cn * @Date 2017/5/24 22:34 */ public static Boolean isWinOs() { String os = System.getProperty("os.name"); if (os.toLowerCase().startsWith("win")) { return true; } else { return false; } } /** * 获取临时目录 * * @author enilu.cn * @Date 2017/5/24 22:35 */ public static String getTempPath() { return System.getProperty("java.io.tmpdir"); } /** * 把一个数转化为int * * @author fengshuonan * @Date 2017/11/15 下午11:10 */ public static Integer toInt(Object val) { if (val instanceof Double) { BigDecimal bigDecimal = new BigDecimal((Double) val); return bigDecimal.intValue(); } else { return Integer.valueOf(val.toString()); } } /** * 获取项目路径 */ public static String getWebRootPath(String filePath) { try { String path = ToolUtil.class.getClassLoader().getResource("").toURI().getPath(); path = path.replace("/WEB-INF/classes/", ""); path = path.replace("/target/classes/", ""); path = path.replace("file:/", ""); if (ToolUtil.isEmpty(filePath)) { return path; } else { return path + "/" + filePath; } } catch (URISyntaxException e) { throw new RuntimeException(e); } } } ================================================ FILE: material-core/src/main/java/cn/enilu/material/utils/WafKit.java ================================================ /** * Copyright (c) 2011-2014, hubin (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.utils; import java.util.regex.Pattern; /** * Web防火墙工具类 *

    * @author hubin * @Date 2014-5-8 */ public class WafKit { /** * @Description 过滤XSS脚本内容 * @param value * 待处理内容 * @return */ public static String stripXSS(String value) { String rlt = null; if (null != value) { // NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to // avoid encoded attacks. // value = ESAPI.encoder().canonicalize(value); // Avoid null characters rlt = value.replaceAll("", ""); // Avoid anything between script tags Pattern scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); rlt = scriptPattern.matcher(rlt).replaceAll(""); // Avoid anything in a src='...' type of expression /*scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); rlt = scriptPattern.matcher(rlt).replaceAll(""); scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); rlt = scriptPattern.matcher(rlt).replaceAll("");*/ // Remove any lonesome tag scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); rlt = scriptPattern.matcher(rlt).replaceAll(""); // Remove any lonesome @} ================================================ FILE: material-generator/src/main/resources/code/view/edit.html.vm ================================================ @layout("/common/include.html"){ #set($item_id='${item.id}') #set($itemLeft = '${item') #set($itemRight = '}')

    <#input id="id" name="自增主键" value="$item_id" readonly="true" underline="true"/>
    #foreach ($column in $table.LabeledColumns)
    <#input id="${column.FieldName}" name="${column.label}" value="$itemLeft.${column.FieldName}$itemRight" />
    #end
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="${table.entityClassName}InfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="${table.entityClassName}InfoDlg.close()"/>
    @} ================================================ FILE: material-generator/src/main/resources/code/view/index.html.vm ================================================ @layout("/common/layout.html"){

    ${table.Label}管理

    #set($ctxPath = '${ctxPath}')
    <#table id="${table.entityClassName}Table"/>
    @} ================================================ FILE: material-generator/src/main/resources/code/view/index.js.vm ================================================ /** * ${table.Label}管理初始化 */ var ${table.entityClassName} = { id: "${table.entityClassName}Table", seItem: null, table: null, layerIndex: -1 }; /** * 初始化表格的列 */ ${table.entityClassName}.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle',sortable:true}, #foreach ($column in $table.LabeledColumns) #if($velocityCount==1) {title: '${column.label}', field: '${column.FieldName}', visible: true, align: 'center', valign: 'middle',sortable:true,formatter:function(data,row){ return ''+data+''; }}, #else {title: '${column.label}', field: '${column.FieldName}', visible: true, align: 'center', valign: 'middle',sortable:true}, #end #end {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加${table.Label} */ ${table.entityClassName}.openAdd${table.entityClassName} = function () { var index = layer.open({ type: 2, title: '添加${table.Label}', area: ['65%', '65%'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '${table.UriPrefix}/add' }); this.layerIndex = index; }; /** * 打开查看${table.Label}详情 */ ${table.entityClassName}.open${table.entityClassName}Detail = function (id) { var index = layer.open({ type: 2, title: '系统参数详情', area: ['65%', '65%'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '${table.UriPrefix}/edit/' + id }); this.layerIndex = index; }; /** * 删除${table.Label} */ ${table.entityClassName}.delete = function (id) { var operation = function() { var ajax = new $ax(Feng.ctxPath + "${table.UriPrefix}/delete", function (data) { Feng.success("删除成功!"); ${table.entityClassName}. table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("id", id); ajax.start(); } Feng.confirm("确认删除该记录?", operation); }; /** * 查询${table.Label}列表 */ ${table.entityClassName}.search = function () { var queryData = {}; queryData['name'] = $("#name").val(); ${table.entityClassName}.table.refresh({query: queryData}); }; /** * 重置查询条件 */ ${table.entityClassName}.reset = function () { $('#name').val(''); this.search(); }; $(function () { var defaultColunms = ${table.entityClassName}.initColumn(); var table = new BSTable(${table.entityClassName}.id, "${table.UriPrefix}/list", defaultColunms); table.setPaginationType("server"); ${table.entityClassName}.table = table.init(); }); ================================================ FILE: material-generator/src/main/resources/code/view/info.js.vm ================================================ /** * 初始化系统参数详情对话框 */ var ${table.entityClassName}InfoDlg = { ${table.entityNameLowerFirstChar}InfoData : {} }; /** * 清除数据 */ ${table.entityClassName}InfoDlg.clearData = function() { this.${table.entityNameLowerFirstChar}InfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ ${table.entityClassName}InfoDlg.set = function(key, val) { this.${table.entityNameLowerFirstChar}InfoData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ ${table.entityClassName}InfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ ${table.entityClassName}InfoDlg.close = function() { parent.layer.close(window.parent.${table.entityClassName}.layerIndex); } /** * 收集数据 */ ${table.entityClassName}InfoDlg.collectData = function() { this .set('id') #foreach ($column in ${table.LabeledColumns}) .set('${column.FieldName}') #end ; } /** * 提交添加 */ ${table.entityClassName}InfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "${table.UriPrefix}/add", function(data){ Feng.success("添加成功!"); window.parent.${table.entityClassName}.table.refresh(); ${table.entityClassName}InfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.${table.entityNameLowerFirstChar}InfoData); ajax.start(); } /** * 提交修改 */ ${table.entityClassName}InfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "${table.UriPrefix}/update", function(data){ Feng.success("修改成功!"); window.parent.${table.entityClassName}.table.refresh(); ${table.entityClassName}InfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.${table.entityNameLowerFirstChar}InfoData); ajax.start(); } $(function() { }); ================================================ FILE: material-lab/README.md ================================================ # material-lab 该模块包含一些实验特性的模块,主要探索spring boot的各种功能 ## 集成actuator监控 - 使用actuator可以方便的对spring boot应用做监控 - 本引用之前的ehcache-core版本过低,需要升级: ```javascript net.sf.ehcache ehcache-core 2.6.11 调整为: net.sf.ehcache.internal ehcache-core 2.10.5 ``` - 启用该功能后 访问http://localhost:8000/actuator/env ================================================ FILE: material-lab/pom.xml ================================================ material-admin cn.enilu 0.1 4.0.0 material-lab org.springframework.boot spring-boot-starter-actuator ================================================ FILE: material-lab/src/main/resources/application.properties ================================================ #actuator ع ##صַ˿ management.server.port=8000 ##springboot2.0֮Http½ĬϵendpointֻΪinfohealthҪ뿪ļعܣҪֶ management.endpoints.web.exposure.include=* ##ǰ׺ Ĭ/actuator management.endpoints.web.base-path=/actuator ================================================ FILE: material-manage/pom.xml ================================================ material-admin cn.enilu 0.1 war 4.0.0 material-manage cn.enilu material-lab ${project.version} cn.enilu material-core ${project.version} org.springframework.boot spring-boot-starter-tomcat compile com.ibeetl beetl io.springfox springfox-swagger2 io.springfox springfox-swagger-ui com.github.penggle kaptcha com.google.zxing core org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true maven-compiler-plugin 1.8 1.8 maven-war-plugin 2.6 material false src/main/webapp src/main/resources src/main/java **/*.xml ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/AdminApplication.java ================================================ package cn.enilu.material.admin; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.dao.BaseRepositoryFactoryBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * SpringBoot方式启动类 * * @author enilu.cn * @Date 2017/5/21 12:06 */ @SpringBootApplication @EnableCaching @ComponentScan(basePackages = "cn.enilu.material") @EntityScan(basePackages="cn.enilu.material.bean.entity") @EnableJpaRepositories(basePackages= "cn.enilu.material.dao", repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class) @EnableJpaAuditing public class AdminApplication extends WebMvcConfigurerAdapter { protected final static Logger logger = LoggerFactory.getLogger(AdminApplication.class); @Autowired AppProperties appProperties; /** * 增加swagger的支持 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (appProperties.getSwaggerOpen()) { registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } } public static void main(String[] args) { SpringApplication.run(AdminApplication.class, args); logger.info("materail-admin is success!"); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/AdminServletInitializer.java ================================================ package cn.enilu.material.admin; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; /** * Web程序启动类 * * @author fengshuonan * @date 2017-05-21 9:43 */ public class AdminServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(AdminApplication.class); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/common/constant/enums/Status.java ================================================ package cn.enilu.material.admin.common.constant.enums; import java.io.Serializable; /** * 通用的业务状态 * * @author zhfish */ public enum Status { 未启用(0), 启用(1); private int value; Status(final int value) { this.value = value; } public Serializable getValue() { return this.value; } @Override public String toString(){ switch (this.value) { case 0: return "未启用"; case 1: return "启用"; } return "未启用"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/common/constant/state/ExpenseState.java ================================================ package cn.enilu.material.admin.common.constant.state; /** * 是否是菜单的枚举 * * @author fengshuonan * @date 2017年6月1日22:50:11 */ public enum ExpenseState { SUBMITING(1, "待提交"), CHECKING(2, "待审核"), PASS(3, "审核通过"), UN_PASS(4, "未通过"); int code; String message; ExpenseState(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer status) { if (status == null) { return ""; } else { for (ExpenseState s : ExpenseState.values()) { if (s.getCode() == status) { return s.getMessage(); } } return ""; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/common/constant/state/IsMenu.java ================================================ package cn.enilu.material.admin.common.constant.state; /** * 是否是菜单的枚举 * * @author fengshuonan * @date 2017年6月1日22:50:11 */ public enum IsMenu { YES(1, "是"), NO(0, "不是");//不是菜单的是按钮 int code; String message; IsMenu(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer status) { if (status == null) { return ""; } else { for (IsMenu s : IsMenu.values()) { if (s.getCode() == status) { return s.getMessage(); } } return ""; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/common/constant/state/MenuOpenStatus.java ================================================ package cn.enilu.material.admin.common.constant.state; /** * 菜单是否打开的状态 * * @author fengshuonan * @Date 2017年4月8日10:12:15 */ public enum MenuOpenStatus { OPEN(1, "打开"), CLOSE(0, "关闭"); int code; String message; MenuOpenStatus(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static String valueOf(Integer status) { if (status == null) { return ""; } else { for (MenuOpenStatus s : MenuOpenStatus.values()) { if (s.getCode() == status) { return s.getMessage(); } } return ""; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/DefaultFastjsonConfig.java ================================================ package cn.enilu.material.admin.config; import com.alibaba.fastjson.serializer.SerializeConfig; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.ToStringSerializer; import com.alibaba.fastjson.serializer.ValueFilter; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import java.math.BigInteger; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; /** * fastjson配置类 * * @author fengshuonan * @date 2017-05-23 22:56 */ @Configuration("defaultFastjsonConfig") @ConditionalOnClass(com.alibaba.fastjson.JSON.class) @ConditionalOnMissingBean(FastJsonHttpMessageConverter.class) @ConditionalOnWebApplication public class DefaultFastjsonConfig { @Bean public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); converter.setFastJsonConfig(fastjsonConfig()); converter.setSupportedMediaTypes(getSupportedMediaType()); return converter; } /** * fastjson的配置 */ public FastJsonConfig fastjsonConfig() { FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteEnumUsingToString, SerializerFeature.DisableCircularReferenceDetect ); fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); ValueFilter valueFilter = new ValueFilter() { public Object process(Object o, String s, Object o1) { if (null == o1) { o1 = ""; } return o1; } }; fastJsonConfig.setCharset(Charset.forName("utf-8")); fastJsonConfig.setSerializeFilters(valueFilter); //解决Long转json精度丢失的问题 SerializeConfig serializeConfig = SerializeConfig.globalInstance; serializeConfig.put(BigInteger.class, ToStringSerializer.instance); serializeConfig.put(Long.class, ToStringSerializer.instance); serializeConfig.put(Long.TYPE, ToStringSerializer.instance); fastJsonConfig.setSerializeConfig(serializeConfig); return fastJsonConfig; } /** * 支持的mediaType类型 */ public List getSupportedMediaType() { ArrayList mediaTypes = new ArrayList<>(); mediaTypes.add(MediaType.APPLICATION_JSON_UTF8); return mediaTypes; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/DruidConfig.java ================================================ package cn.enilu.material.admin.config; import cn.enilu.material.admin.core.xss.XssFilter; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import com.alibaba.druid.support.spring.stat.BeanTypeAutoProxyCreator; import com.alibaba.druid.support.spring.stat.DruidStatInterceptor; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; import cn.enilu.material.web.listener.ConfigListener; import org.springframework.aop.Advisor; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.JdkRegexpMethodPointcut; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextListener; import java.util.Arrays; import java.util.Properties; /** * web 配置类 * * @author fengshuonan * @date 2016年11月12日 下午5:03:32 */ @Configuration public class DruidConfig { /** * druidServlet注册 */ @Bean public ServletRegistrationBean druidServletRegistration() { ServletRegistrationBean registration = new ServletRegistrationBean(new StatViewServlet()); registration.addUrlMappings("/druid/*"); return registration; } /** * druid监控 配置URI拦截策略 * @return */ @Bean public FilterRegistrationBean druidStatFilter(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter()); //添加过滤规则. filterRegistrationBean.addUrlPatterns("/*"); //添加不需要忽略的格式信息. filterRegistrationBean.addInitParameter( "exclusions","/static/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid,/druid/*"); //用于session监控页面的用户名显示 需要登录后主动将username注入到session里 filterRegistrationBean.addInitParameter("principalSessionName","username"); return filterRegistrationBean; } /** * druid数据库连接池监控 */ @Bean public DruidStatInterceptor druidStatInterceptor() { return new DruidStatInterceptor(); } @Bean public JdkRegexpMethodPointcut druidStatPointcut(){ JdkRegexpMethodPointcut druidStatPointcut = new JdkRegexpMethodPointcut(); String patterns = "cn.enilu.material.admin.modular.*.code.*"; //可以set多个 druidStatPointcut.setPatterns(patterns); return druidStatPointcut; } /** * druid数据库连接池监控 */ @Bean public BeanTypeAutoProxyCreator beanTypeAutoProxyCreator() { BeanTypeAutoProxyCreator beanTypeAutoProxyCreator = new BeanTypeAutoProxyCreator(); beanTypeAutoProxyCreator.setTargetBeanType(DruidDataSource.class); beanTypeAutoProxyCreator.setInterceptorNames("druidStatInterceptor"); return beanTypeAutoProxyCreator; } /** * druid 为druidStatPointcut添加拦截 * @return */ @Bean public Advisor druidStatAdvisor() { return new DefaultPointcutAdvisor(druidStatPointcut(), druidStatInterceptor()); } /** * xssFilter注册 */ @Bean public FilterRegistrationBean xssFilterRegistration() { XssFilter xssFilter = new XssFilter(); xssFilter.setUrlExclusion(Arrays.asList("/notice/update","/notice/add")); FilterRegistrationBean registration = new FilterRegistrationBean(xssFilter); registration.addUrlPatterns("/*"); return registration; } /** * RequestContextListener注册 */ @Bean public ServletListenerRegistrationBean requestContextListenerRegistration() { return new ServletListenerRegistrationBean<>(new RequestContextListener()); } /** * ConfigListener注册 */ @Bean public ServletListenerRegistrationBean configListenerRegistration() { return new ServletListenerRegistrationBean<>(new ConfigListener()); } /** * 验证码生成相关 */ @Bean public DefaultKaptcha kaptcha() { Properties properties = new Properties(); properties.put("kaptcha.border", "no"); properties.put("kaptcha.border.color", "105,179,90"); properties.put("kaptcha.textproducer.font.color", "blue"); properties.put("kaptcha.image.width", "125"); properties.put("kaptcha.image.height", "45"); properties.put("kaptcha.textproducer.font.size", "45"); properties.put("kaptcha.session.key", "code"); properties.put("kaptcha.textproducer.char.length", "4"); properties.put("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); Config config = new Config(properties); DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); defaultKaptcha.setConfig(config); return defaultKaptcha; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/EhCacheConfig.java ================================================ package cn.enilu.material.admin.config; import net.sf.ehcache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; /** * ehcache配置 * * @author fengshuonan * @date 2017-05-20 23:11 */ @Configuration @EnableCaching public class EhCacheConfig { /** * EhCache的配置 */ @Bean public EhCacheCacheManager cacheManager(CacheManager cacheManager) { return new EhCacheCacheManager(cacheManager); } /** * EhCache的配置 */ @Bean public EhCacheManagerFactoryBean ehcache() { EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean(); ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); return ehCacheManagerFactoryBean; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/SpringSessionConfig.java ================================================ package cn.enilu.material.admin.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; /** * spring session配置 * * @author fengshuonan * @date 2017-07-13 21:05 */ //@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) //session过期时间 如果部署多机环境,需要打开注释 @ConditionalOnProperty(prefix = "apps", name = "spring-session-open", havingValue = "true") public class SpringSessionConfig { } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/SwaggerConfig.java ================================================ package cn.enilu.material.admin.config; import io.swagger.annotations.ApiOperation; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * swagger配置类 * * @author fengshuonan * @date 2017年6月1日19:42:59 */ @Configuration @EnableSwagger2 @ConditionalOnProperty(prefix = "apps", name = "swagger-open", havingValue = "true") public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) //这里采用包含注解的方式来确定要显示的接口 .apis(RequestHandlerSelectors.basePackage("cn.enilu.material.admin.modular")) //这里采用包扫描的方式来确定要显示的接口 .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("material-admin Doc") .description("material-admin Api文档") .termsOfServiceUrl("https://enilu.gitee.io/material-admin") .contact("www.enilu.cn") .version("2.0") .build(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/UserIDAuditorConfig.java ================================================ package cn.enilu.material.admin.config; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.shiro.ShiroKit; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; import java.util.Optional; /** * UserIDAuditorBean * * @author zt * @version 2019/1/8 0008 */ @Configuration public class UserIDAuditorConfig implements AuditorAware { @Override public Optional getCurrentAuditor() { ShiroUser shiroUser = ShiroKit.getUser(); if(shiroUser!=null){ return Optional.of(shiroUser.getId()); } return Optional.of(Const.SYSTEM_USER_ID); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/properties/AppProperties.java ================================================ package cn.enilu.material.admin.config.properties; import cn.enilu.material.utils.ToolUtil; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.io.File; /** * 项目配置 * * @author enilu.cn * @Date 2017/5/23 22:31 */ @Component @ConfigurationProperties(prefix = AppProperties.PREFIX) public class AppProperties { public static final String PREFIX = "apps"; private Boolean kaptchaOpen = false; private Boolean swaggerOpen = false; private String fileUploadPath; private Boolean haveCreatePath = false; private Boolean springSessionOpen = false; private Integer sessionInvalidateTime = 30 * 60; //session 失效时间(默认为30分钟 单位:秒) private Integer sessionValidationInterval = 15 * 60; //session 验证失效时间(默认为15分钟 单位:秒) public String getFileUploadPath() { //如果没有写文件上传路径,保存到临时目录 if (ToolUtil.isEmpty(fileUploadPath)) { return ToolUtil.getTempPath(); } else { //判断有没有结尾符,没有得加上 if (!fileUploadPath.endsWith(File.separator)) { fileUploadPath = fileUploadPath + File.separator; } //判断目录存不存在,不存在得加上 if (haveCreatePath == false) { File file = new File(fileUploadPath); file.mkdirs(); haveCreatePath = true; } return fileUploadPath; } } public void setFileUploadPath(String fileUploadPath) { this.fileUploadPath = fileUploadPath; } public Boolean getKaptchaOpen() { return kaptchaOpen; } public void setKaptchaOpen(Boolean kaptchaOpen) { this.kaptchaOpen = kaptchaOpen; } public Boolean getSwaggerOpen() { return swaggerOpen; } public void setSwaggerOpen(Boolean swaggerOpen) { this.swaggerOpen = swaggerOpen; } public Boolean getSpringSessionOpen() { return springSessionOpen; } public void setSpringSessionOpen(Boolean springSessionOpen) { this.springSessionOpen = springSessionOpen; } public Integer getSessionInvalidateTime() { return sessionInvalidateTime; } public void setSessionInvalidateTime(Integer sessionInvalidateTime) { this.sessionInvalidateTime = sessionInvalidateTime; } public Integer getSessionValidationInterval() { return sessionValidationInterval; } public void setSessionValidationInterval(Integer sessionValidationInterval) { this.sessionValidationInterval = sessionValidationInterval; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/properties/BeetlProperties.java ================================================ package cn.enilu.material.admin.config.properties; import cn.enilu.material.utils.ToolUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.Properties; /** * beetl配置(如果需要配置别的配置可参照这个形式自己添加) * * @author fengshuonan * @date 2017-05-24 20:37 */ @Configuration @ConfigurationProperties(prefix = BeetlProperties.BEETLCONF_PREFIX) public class BeetlProperties { public static final String BEETLCONF_PREFIX = "beetl"; private String delimiterStatementStart; private String delimiterStatementEnd; private String resourceTagroot; private String resourceTagsuffix; private String resourceAutoCheck; @Value("${spring.mvc.view.prefix}") private String prefix; public Properties getProperties(){ Properties properties = new Properties(); if(ToolUtil.isNotEmpty(delimiterStatementStart)){ if(delimiterStatementStart.startsWith("\\")){ delimiterStatementStart = delimiterStatementStart.substring(1); } properties.setProperty("DELIMITER_STATEMENT_START",delimiterStatementStart); } if(ToolUtil.isNotEmpty(delimiterStatementEnd)){ properties.setProperty("DELIMITER_STATEMENT_END",delimiterStatementEnd); }else{ properties.setProperty("DELIMITER_STATEMENT_END","null"); } if(ToolUtil.isNotEmpty(resourceTagroot)){ properties.setProperty("RESOURCE.tagRoot",resourceTagroot); } if(ToolUtil.isNotEmpty(resourceTagsuffix)){ properties.setProperty("RESOURCE.tagSuffix",resourceTagsuffix); } if(ToolUtil.isNotEmpty(resourceAutoCheck)){ properties.setProperty("RESOURCE.autoCheck",resourceAutoCheck); } return properties; } public String getPrefix() { return prefix; } public String getDelimiterStatementStart() { return delimiterStatementStart; } public void setDelimiterStatementStart(String delimiterStatementStart) { this.delimiterStatementStart = delimiterStatementStart; } public String getDelimiterStatementEnd() { return delimiterStatementEnd; } public void setDelimiterStatementEnd(String delimiterStatementEnd) { this.delimiterStatementEnd = delimiterStatementEnd; } public String getResourceTagroot() { return resourceTagroot; } public void setResourceTagroot(String resourceTagroot) { this.resourceTagroot = resourceTagroot; } public String getResourceTagsuffix() { return resourceTagsuffix; } public void setResourceTagsuffix(String resourceTagsuffix) { this.resourceTagsuffix = resourceTagsuffix; } public String getResourceAutoCheck() { return resourceAutoCheck; } public void setResourceAutoCheck(String resourceAutoCheck) { this.resourceAutoCheck = resourceAutoCheck; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/web/BeetlConfig.java ================================================ package cn.enilu.material.admin.config.web; import cn.enilu.material.admin.config.properties.BeetlProperties; import cn.enilu.material.admin.core.beetl.BeetlConfiguration; import org.beetl.core.resource.ClasspathResourceLoader; import org.beetl.ext.spring.BeetlSpringViewResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * web 配置类 * * @author fengshuonan * @date 2016年11月12日 下午5:03:32 */ @Configuration public class BeetlConfig { @Autowired BeetlProperties beetlProperties; /** * beetl的配置 */ @Bean(initMethod = "init") public BeetlConfiguration beetlConfiguration() { BeetlConfiguration beetlConfiguration = new BeetlConfiguration(); beetlConfiguration.setResourceLoader(new ClasspathResourceLoader(BeetlConfig.class.getClassLoader(), beetlProperties.getPrefix())); beetlConfiguration.setConfigProperties(beetlProperties.getProperties()); return beetlConfiguration; } /** * beetl的视图解析器 */ @Bean public BeetlSpringViewResolver beetlViewResolver() { BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver(); beetlSpringViewResolver.setConfig(beetlConfiguration()); beetlSpringViewResolver.setContentType("text/html;charset=UTF-8"); beetlSpringViewResolver.setOrder(0); return beetlSpringViewResolver; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/config/web/ShiroConfig.java ================================================ package cn.enilu.material.admin.config.web; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.shiro.ShiroDbRealm; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.codec.Base64; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.Cookie; import org.apache.shiro.web.servlet.ShiroHttpSession; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.session.mgt.ServletContainerSessionManager; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * shiro权限管理的配置 * * @author fengshuonan * @date 2016年11月14日 下午3:03:44 */ @Configuration public class ShiroConfig { /** * 安全管理器 */ @Bean public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(this.shiroDbRealm()); securityManager.setCacheManager(cacheShiroManager); securityManager.setRememberMeManager(rememberMeManager); securityManager.setSessionManager(sessionManager); return securityManager; } /** * spring session管理器(多机环境) */ @Bean @ConditionalOnProperty(prefix = "apps", name = "spring-session-open", havingValue = "true") public ServletContainerSessionManager servletContainerSessionManager() { return new ServletContainerSessionManager(); } /** * session管理器(单机环境) */ @Bean @ConditionalOnProperty(prefix = "apps", name = "spring-session-open", havingValue = "false") public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager, AppProperties appProperties) { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setCacheManager(cacheShiroManager); sessionManager.setSessionValidationInterval(appProperties.getSessionValidationInterval() * 1000); sessionManager.setGlobalSessionTimeout(appProperties.getSessionInvalidateTime() * 1000); sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME); cookie.setName("shiroCookie"); cookie.setHttpOnly(true); sessionManager.setSessionIdCookie(cookie); return sessionManager; } /** * 缓存管理器 使用Ehcache实现 */ @Bean public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) { EhCacheManager ehCacheManager = new EhCacheManager(); ehCacheManager.setCacheManager(ehcache.getObject()); return ehCacheManager; } /** * 项目自定义的Realm */ @Bean public ShiroDbRealm shiroDbRealm() { return new ShiroDbRealm(); } /** * rememberMe管理器, cipherKey生成见{@code Base64Test.java} */ @Bean public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) { CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA==")); manager.setCookie(rememberMeCookie); return manager; } /** * 记住密码Cookie */ @Bean public SimpleCookie rememberMeCookie() { SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); simpleCookie.setHttpOnly(true); simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天 return simpleCookie; } /** * Shiro的过滤器链 */ @Bean public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); /** * 默认的登陆访问url */ shiroFilter.setLoginUrl("/login"); /** * 登陆成功后跳转的url */ shiroFilter.setSuccessUrl("/"); /** * 没有权限跳转的url */ shiroFilter.setUnauthorizedUrl("/global/error"); /** * 配置shiro拦截器链 * * anon 不需要认证 * authc 需要认证 * user 验证通过或RememberMe登录的都可以 * * 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的 * * 顺序从上到下,优先级依次降低 * */ Map hashMap = new LinkedHashMap<>(); hashMap.put("/static/**", "anon"); hashMap.put("/login", "anon"); hashMap.put("/global/sessionError", "anon"); hashMap.put("/kaptcha", "anon"); hashMap.put("/**", "user"); shiroFilter.setFilterChainDefinitionMap(hashMap); return shiroFilter; } /** * 在方法中 注入 securityManager,进行代理控制 */ @Bean public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) { MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean(); bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager"); bean.setArguments(new Object[]{securityManager}); return bean; } /** * Shiro生命周期处理器: * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm) * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager) */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 启用shrio授权注解拦截方式,AOP式方法级权限检查 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/CoreFlag.java ================================================ package cn.enilu.material.admin.core; /** * 此类用来获取core模块的包路径 * * @author fengshuonan * @Date 2017/12/5 下午12:44 */ public class CoreFlag { } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/aop/BaseControllerExceptionHandler.java ================================================ package cn.enilu.material.admin.core.aop; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.admin.core.base.tips.ErrorTip; import cn.enilu.material.bean.exception.ExceptionEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; /** * 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截) * * @author fengshuonan * @date 2016年11月12日 下午3:19:56 */ public class BaseControllerExceptionHandler { private Logger log = LoggerFactory.getLogger(this.getClass()); /** * 拦截业务异常 * * @author fengshuonan */ @ExceptionHandler(ApplicationException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ErrorTip notFount(ApplicationException e) { log.error("业务异常:", e); return new ErrorTip(e.getCode(), e.getMessage()); } /** * 拦截未知的运行时异常 * * @author fengshuonan */ @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ErrorTip notFount(RuntimeException e) { log.error("运行时异常:", e); return new ErrorTip(ExceptionEnum.SERVER_ERROR.getCode(), ExceptionEnum.SERVER_ERROR.getMessage()); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/aop/GlobalExceptionHandler.java ================================================ package cn.enilu.material.admin.core.aop; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.platform.log.LogManager; import cn.enilu.material.platform.log.LogTaskFactory; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.bean.exception.InvalidKaptchaException; import cn.enilu.material.admin.core.base.tips.ErrorTip; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.CredentialsException; import org.apache.shiro.authc.DisabledAccountException; import org.apache.shiro.session.InvalidSessionException; import org.apache.shiro.session.UnknownSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.UndeclaredThrowableException; /** * 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截) * * @author fengshuonan * @date 2016年11月12日 下午3:19:56 */ @ControllerAdvice public class GlobalExceptionHandler { private Logger log = LoggerFactory.getLogger(this.getClass()); /** * 拦截业务异常 * * @author fengshuonan */ @ExceptionHandler(ApplicationException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ErrorTip notFount(ApplicationException e) { LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e)); HttpKit.getRequest().setAttribute("tip", e.getMessage()); log.error("业务异常:", e); return new ErrorTip(e.getCode(), e.getMessage()); } /** * 用户未登录 * * @author fengshuonan */ @ExceptionHandler(AuthenticationException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) public String unAuth(AuthenticationException e) { log.error("用户未登陆:", e); return "/login.html"; } /** * 账号被冻结 * * @author fengshuonan */ @ExceptionHandler(DisabledAccountException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) public String accountLocked(DisabledAccountException e, Model model) { String username = HttpKit.getRequest().getParameter("username"); LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号被冻结", HttpKit.getIp())); model.addAttribute("tips", "账号被冻结"); return "/login.html"; } /** * 账号密码错误 * * @author fengshuonan */ @ExceptionHandler(CredentialsException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) public String credentials(CredentialsException e, Model model) { String username = HttpKit.getRequest().getParameter("username"); LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号密码错误", HttpKit.getIp())); model.addAttribute("tips", "账号密码错误"); return "/login.html"; } /** * 验证码错误 * * @author fengshuonan */ @ExceptionHandler(InvalidKaptchaException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String credentials(InvalidKaptchaException e, Model model) { String username = HttpKit.getRequest().getParameter("username"); LogManager.me().executeLog(LogTaskFactory.loginLog(username, "验证码错误", HttpKit.getIp())); model.addAttribute("tips", "验证码错误"); return "/login.html"; } /** * 无权访问该资源 * * @author fengshuonan */ @ExceptionHandler(UndeclaredThrowableException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) @ResponseBody public ErrorTip credentials(UndeclaredThrowableException e) { HttpKit.getRequest().setAttribute("tip", "权限异常"); log.error("权限异常!", e); return new ErrorTip(BizExceptionEnum.NO_PERMITION.getCode(),BizExceptionEnum.NO_PERMITION.getMessage()); } /** * 拦截未知的运行时异常 * * @author fengshuonan */ @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public ErrorTip notFount(RuntimeException e) { LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e)); HttpKit.getRequest().setAttribute("tip", "服务器未知运行时异常"); log.error("运行时异常:", e); return new ErrorTip(BizExceptionEnum.SERVER_ERROR.getCode(),BizExceptionEnum.SERVER_ERROR.getMessage()); } /** * session失效的异常拦截 * * @author enilu.cn * @Date 2017/6/7 21:02 */ @ExceptionHandler(InvalidSessionException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String sessionTimeout(InvalidSessionException e, Model model, HttpServletRequest request, HttpServletResponse response) { model.addAttribute("tips", "session超时"); assertAjax(request, response); return "/login.html"; } /** * session异常 * * @author enilu.cn * @Date 2017/6/7 21:02 */ @ExceptionHandler(UnknownSessionException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String sessionTimeout(UnknownSessionException e, Model model, HttpServletRequest request, HttpServletResponse response) { model.addAttribute("tips", "session超时"); assertAjax(request, response); return "/login.html"; } private void assertAjax(HttpServletRequest request, HttpServletResponse response) { if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) { //如果是ajax请求响应头会有,x-requested-with response.setHeader("sessionstatus", "timeout");//在响应头设置session状态 } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/controller/BaseController.java ================================================ package cn.enilu.material.admin.core.base.controller; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.admin.core.util.FileUtil; import cn.enilu.material.admin.core.base.tips.SuccessTip; import cn.enilu.material.warpper.BaseControllerWarpper; import cn.enilu.material.admin.core.page.PageInfoBT; import cn.enilu.material.bean.vo.query.Page; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.UnsupportedEncodingException; public class BaseController { protected static String SUCCESS = "SUCCESS"; protected static String ERROR = "ERROR"; protected static String REDIRECT = "redirect:"; protected static String FORWARD = "forward:"; protected static SuccessTip SUCCESS_TIP = new SuccessTip(); protected HttpServletRequest getHttpServletRequest() { return HttpKit.getRequest(); } protected HttpServletResponse getHttpServletResponse() { return HttpKit.getResponse(); } protected HttpSession getSession() { return HttpKit.getRequest().getSession(); } protected HttpSession getSession(Boolean flag) { return HttpKit.getRequest().getSession(flag); } protected String getPara(String name) { return HttpKit.getRequest().getParameter(name); } protected void setAttr(String name, Object value) { HttpKit.getRequest().setAttribute(name, value); } protected Integer getSystemInvokCount() { return (Integer) this.getHttpServletRequest().getServletContext().getAttribute("systemCount"); } /** * 把service层的分页信息,封装为bootstrap table通用的分页封装 */ protected PageInfoBT packForBT(Page page) { return new PageInfoBT(page); } /** * 包装一个list,让list增加额外属性 */ protected Object warpObject(BaseControllerWarpper warpper) { return warpper.warp(); } /** * 删除cookie */ protected void deleteCookieByName(String cookieName) { Cookie[] cookies = this.getHttpServletRequest().getCookies(); for (Cookie cookie : cookies) { if (cookie.getName().equals(cookieName)) { Cookie temp = new Cookie(cookie.getName(), ""); temp.setMaxAge(0); this.getHttpServletResponse().addCookie(temp); } } } /** * 返回前台文件流 * * @author fengshuonan * @date 2017年2月28日 下午2:53:19 */ protected ResponseEntity renderFile(String fileName, String filePath) { byte[] bytes = FileUtil.toByteArray(filePath); return renderFile(fileName, bytes); } /** * 返回前台文件流 * * @author fengshuonan * @date 2017年2月28日 下午2:53:19 */ protected ResponseEntity renderFile(String fileName, byte[] fileBytes) { String dfileName = null; try { dfileName = new String(fileName.getBytes("gb2312"), "iso8859-1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDispositionFormData("attachment", dfileName); return new ResponseEntity(fileBytes, headers, HttpStatus.CREATED); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/controller/ErrorView.java ================================================ package cn.enilu.material.admin.core.base.controller; import org.springframework.stereotype.Component; import org.springframework.web.servlet.View; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; /** * 错误页面的默认跳转(例如请求404的时候,默认走这个视图解析器) * * @author fengshuonan * @date 2017-05-21 11:34 */ @Component("error") public class ErrorView implements View { @Override public String getContentType() { return "text/html"; } @Override public void render(Map map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { httpServletRequest.getRequestDispatcher("/global/error").forward(httpServletRequest, httpServletResponse); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/controller/GlobalController.java ================================================ package cn.enilu.material.admin.core.base.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; /** * 全局的控制器 * * @author fengshuonan * @date 2016年11月13日 下午11:04:45 */ @Controller @RequestMapping("/global") public class GlobalController { /** * 跳转到404页面 * * @author fengshuonan */ @RequestMapping(path = "/error") public String errorPage() { return "/404.html"; } /** * 跳转到session超时页面 * * @author fengshuonan */ @RequestMapping(path = "/sessionError") public String errorPageInfo(Model model) { model.addAttribute("tips", "session超时"); return "/login.html"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/tips/ErrorTip.java ================================================ package cn.enilu.material.admin.core.base.tips; /** * 返回给前台的错误提示 * * @author fengshuonan * @date 2016年11月12日 下午5:05:22 */ public class ErrorTip extends Tip { public ErrorTip(int code, String message) { super(); this.code = code; this.message = message; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/tips/SuccessTip.java ================================================ package cn.enilu.material.admin.core.base.tips; /** * 返回给前台的成功提示 * * @author fengshuonan * @date 2016年11月12日 下午5:05:22 */ public class SuccessTip extends Tip { public SuccessTip(){ super.code = 200; super.message = "操作成功"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/base/tips/Tip.java ================================================ package cn.enilu.material.admin.core.base.tips; /** * 返回给前台的提示(最终转化为json形式) * * @author fengshuonan * @Date 2017年1月11日 下午11:58:00 */ public abstract class Tip { protected int code; protected String message; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/beetl/BeetlConfiguration.java ================================================ package cn.enilu.material.admin.core.beetl; import cn.enilu.material.admin.core.util.KaptchaUtil; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.ToolUtil; import org.beetl.ext.spring.BeetlGroupUtilConfiguration; public class BeetlConfiguration extends BeetlGroupUtilConfiguration { @Override public void initOther() { groupTemplate.registerFunctionPackage("shiro", new ShiroExt()); groupTemplate.registerFunctionPackage("tool", new ToolUtil()); groupTemplate.registerFunctionPackage("kaptcha", new KaptchaUtil()); groupTemplate.registerFunctionPackage("constant",ConstantFactory.me()); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/beetl/ShiroExt.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.beetl; import cn.enilu.material.bean.vo.node.MenuNode; import cn.enilu.material.utils.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.beetl.core.GroupTemplate; import cn.enilu.material.bean.core.ShiroUser; import java.util.List; public class ShiroExt { private static final String NAMES_DELIMETER = ","; /** * 获取当前 Subject * * @return Subject */ protected static Subject getSubject() { return SecurityUtils.getSubject(); } /** * 获取封装的 ShiroUser * * @return ShiroUser */ public ShiroUser getUser() { if (isGuest()) { return null; } else { return (ShiroUser) getSubject().getPrincipals().getPrimaryPrincipal(); } } /** * 判断指定url和菜单名称是否是父子关系 * @param sonUrl 子菜单url * @param parentName 父菜单名称 * @return */ public boolean isParent(String sonUrl,String parentName){ if (isGuest()) { return false; } else { ShiroUser shiroUser = (ShiroUser) getSubject().getPrincipals().getPrimaryPrincipal(); List list = shiroUser.getTitles(); for(MenuNode parent:list){ List children = parent.getChildren(); if(children==null || children.isEmpty()){ continue; } for(MenuNode child:children){ if(sonUrl.equals(child.getUrl()) && parentName.equals(parent.getName())){ return true; } } } } return false; } /** * 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用 * * @param roleName 角色名 * @return 属于该角色:true,否则false */ public boolean hasRole(String roleName) { return getSubject() != null && roleName != null && roleName.length() > 0 && getSubject().hasRole(roleName); } /** * 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 * * @param roleName 角色名 * @return 不属于该角色:true,否则false */ public boolean lacksRole(String roleName) { return !hasRole(roleName); } /** * 验证当前用户是否属于以下任意一个角色。 * * @param roleNames 角色列表 * @return 属于:true,否则false */ public boolean hasAnyRoles(String roleNames) { boolean hasAnyRole = false; Subject subject = getSubject(); if (subject != null && roleNames != null && roleNames.length() > 0) { for (String role : roleNames.split(NAMES_DELIMETER)) { if (subject.hasRole(role.trim())) { hasAnyRole = true; break; } } } return hasAnyRole; } /** * 验证当前用户是否属于以下所有角色。 * * @param roleNames 角色列表 * @return 属于:true,否则false */ public boolean hasAllRoles(String roleNames) { boolean hasAllRole = true; Subject subject = getSubject(); if (subject != null && roleNames != null && roleNames.length() > 0) { for (String role : roleNames.split(NAMES_DELIMETER)) { if (!subject.hasRole(role.trim())) { hasAllRole = false; break; } } } return hasAllRole; } /** * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用 * * @param permission 权限名 * @return 拥有权限:true,否则false */ public boolean hasPermission(String permission) { return getSubject() != null && permission != null && permission.length() > 0 && getSubject().isPermitted(permission); } /** * 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 * * @param permission 权限名 * @return 拥有权限:true,否则false */ public boolean lacksPermission(String permission) { return !hasPermission(permission); } /** * 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用 * * @return 通过身份验证:true,否则false */ public boolean authenticated() { return getSubject() != null && getSubject().isAuthenticated(); } /** * 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。 * * @return 没有通过身份验证:true,否则false */ public boolean notAuthenticated() { return !authenticated(); } /** * 认证通过或已记住的用户。与guset搭配使用。 * * @return 用户:true,否则 false */ public boolean isUser() { return getSubject() != null && getSubject().getPrincipal() != null; } /** * 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用 * * @return 访客:true,否则false */ public boolean isGuest() { return !isUser(); } /** * 输出当前用户信息,通常为登录帐号信息。 * * @return 当前用户信息 */ public String principal() { if (getSubject() != null) { Object principal = getSubject().getPrincipal(); return principal.toString(); } return ""; } public static void main(String[] args) { GroupTemplate gt = new GroupTemplate(); gt.registerFunctionPackage("shiro", new ShiroExt()); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/cache/BaseCacheFactory.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.cache; /** * 缓存工厂基类 */ public abstract class BaseCacheFactory implements ICache { @SuppressWarnings("unchecked") public T get(String cacheName, Object key, ILoader iLoader) { Object data = get(cacheName, key); if (data == null) { data = iLoader.load(); put(cacheName, key, data); } return (T) data; } @SuppressWarnings("unchecked") public T get(String cacheName, Object key, Class iLoaderClass) { Object data = get(cacheName, key); if (data == null) { try { ILoader dataLoader = iLoaderClass.newInstance(); data = dataLoader.load(); put(cacheName, key, data); } catch (Exception e) { throw new RuntimeException(e); } } return (T) data; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/cache/CacheKit.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.cache; import java.util.List; /** * 缓存工具类 */ public class CacheKit { private static ICache defaultCacheFactory = new EhcacheFactory(); public static void put(String cacheName, Object key, Object value) { defaultCacheFactory.put(cacheName, key, value); } public static T get(String cacheName, Object key) { return defaultCacheFactory.get(cacheName, key); } @SuppressWarnings("rawtypes") public static List getKeys(String cacheName) { return defaultCacheFactory.getKeys(cacheName); } public static void remove(String cacheName, Object key) { defaultCacheFactory.remove(cacheName, key); } public static void removeAll(String cacheName) { defaultCacheFactory.removeAll(cacheName); } public static T get(String cacheName, Object key, ILoader iLoader) { return defaultCacheFactory.get(cacheName, key, iLoader); } public static T get(String cacheName, Object key, Class iLoaderClass) { return defaultCacheFactory.get(cacheName, key, iLoaderClass); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/cache/EhcacheFactory.java ================================================ /** * Copyright (c) 2011-2016, James Zhan 詹波 (jfinal@126.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.cache; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; /** * Ehcache缓存工厂 */ public class EhcacheFactory extends BaseCacheFactory { private static CacheManager cacheManager; private static volatile Object locker = new Object(); private static final Logger log = LoggerFactory.getLogger(EhcacheFactory.class); private static CacheManager getCacheManager() { if (cacheManager == null) { synchronized (EhcacheFactory.class) { if (cacheManager == null) { cacheManager = CacheManager.create(); } } } return cacheManager; } static Cache getOrAddCache(String cacheName) { CacheManager cacheManager = getCacheManager(); Cache cache = cacheManager.getCache(cacheName); if (cache == null) { synchronized(locker) { cache = cacheManager.getCache(cacheName); if (cache == null) { log.warn("无法找到缓存 [" + cacheName + "]的配置, 使用默认配置."); cacheManager.addCacheIfAbsent(cacheName); cache = cacheManager.getCache(cacheName); log.debug("缓存 [" + cacheName + "] 启动."); } } } return cache; } public void put(String cacheName, Object key, Object value) { getOrAddCache(cacheName).put(new Element(key, value)); } @SuppressWarnings("unchecked") public T get(String cacheName, Object key) { Element element = getOrAddCache(cacheName).get(key); return element != null ? (T)element.getObjectValue() : null; } @SuppressWarnings("rawtypes") public List getKeys(String cacheName) { return getOrAddCache(cacheName).getKeys(); } public void remove(String cacheName, Object key) { getOrAddCache(cacheName).remove(key); } public void removeAll(String cacheName) { getOrAddCache(cacheName).removeAll(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/cache/ICache.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.cache; import java.util.List; /** * 通用缓存接口 */ public interface ICache { void put(String cacheName, Object key, Object value); T get(String cacheName, Object key); @SuppressWarnings("rawtypes") List getKeys(String cacheName); void remove(String cacheName, Object key); void removeAll(String cacheName); T get(String cacheName, Object key, ILoader iLoader); T get(String cacheName, Object key, Class iLoaderClass); } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/cache/ILoader.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.cache; /** * 数据重载 */ public interface ILoader { Object load(); } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/datascope/DataScope.java ================================================ package cn.enilu.material.admin.core.datascope; import java.util.List; /** * 数据范围 * * @author fengshuonan * @date 2017-07-23 22:19 */ public class DataScope { /** * 限制范围的字段名称 */ private String scopeName = "deptid"; /** * 具体的数据范围 */ private List deptIds; public DataScope() { } public DataScope(List deptIds) { this.deptIds = deptIds; } public DataScope(String scopeName, List deptIds) { this.scopeName = scopeName; this.deptIds = deptIds; } public List getDeptIds() { return deptIds; } public void setDeptIds(List deptIds) { this.deptIds = deptIds; } public String getScopeName() { return scopeName; } public void setScopeName(String scopeName) { this.scopeName = scopeName; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/datasource/DruidProperties.java ================================================ package cn.enilu.material.admin.core.datasource; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.sql.SQLException; /** *

    数据库数据源配置

    *

    说明:这个类中包含了许多默认配置,若这些配置符合您的情况,您可以不用管,若不符合,建议不要修改本类,建议直接在"application.yml"中配置即可

    * @author enilu.cn * @date 2017-05-21 11:18 */ @Component @ConfigurationProperties(prefix = "spring.datasource") public class DruidProperties { private String url = "jdbc:mysql://127.0.0.1:3306/material?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private Integer initialSize = 2; private Integer minIdle = 1; private Integer maxActive = 20; private Integer maxWait = 60000; private Integer timeBetweenEvictionRunsMillis = 60000; private Integer minEvictableIdleTimeMillis = 300000; private String validationQuery = "SELECT 'x'"; private Boolean testWhileIdle = true; private Boolean testOnBorrow = false; private Boolean testOnReturn = false; private Boolean poolPreparedStatements = true; private Integer maxPoolPreparedStatementPerConnectionSize = 20; private String filters = "stat"; public void config(DruidDataSource dataSource) { dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setInitialSize(initialSize); //定义初始连接数 dataSource.setMinIdle(minIdle); //最小空闲 dataSource.setMaxActive(maxActive); //定义最大连接数 dataSource.setMaxWait(maxWait); //最长等待时间 // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 配置一个连接在池中最小生存的时间,单位是毫秒 dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); dataSource.setValidationQuery(validationQuery); dataSource.setTestWhileIdle(testWhileIdle); dataSource.setTestOnBorrow(testOnBorrow); dataSource.setTestOnReturn(testOnReturn); // 打开PSCache,并且指定每个连接上PSCache的大小 dataSource.setPoolPreparedStatements(poolPreparedStatements); dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); try { dataSource.setFilters(filters); } catch (SQLException e) { e.printStackTrace(); } } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public Integer getInitialSize() { return initialSize; } public void setInitialSize(Integer initialSize) { this.initialSize = initialSize; } public Integer getMinIdle() { return minIdle; } public void setMinIdle(Integer minIdle) { this.minIdle = minIdle; } public Integer getMaxActive() { return maxActive; } public void setMaxActive(Integer maxActive) { this.maxActive = maxActive; } public Integer getMaxWait() { return maxWait; } public void setMaxWait(Integer maxWait) { this.maxWait = maxWait; } public Integer getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public Integer getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public Boolean getTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(Boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public Boolean getTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(Boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public Boolean getTestOnReturn() { return testOnReturn; } public void setTestOnReturn(Boolean testOnReturn) { this.testOnReturn = testOnReturn; } public Boolean getPoolPreparedStatements() { return poolPreparedStatements; } public void setPoolPreparedStatements(Boolean poolPreparedStatements) { this.poolPreparedStatements = poolPreparedStatements; } public Integer getMaxPoolPreparedStatementPerConnectionSize() { return maxPoolPreparedStatementPerConnectionSize; } public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) { this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/intercept/SessionInterceptor.java ================================================ package cn.enilu.material.admin.core.intercept; import cn.enilu.material.admin.core.util.HttpSessionHolder; import cn.enilu.material.admin.core.base.controller.BaseController; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * 静态调用session的拦截器 * * @author fengshuonan * @date 2016年11月13日 下午10:15:42 */ @Aspect @Component public class SessionInterceptor extends BaseController { @Pointcut("execution(* cn.enilu.material.admin.*..controller.*.*(..))") public void cutService() { } @Around("cutService()") public Object sessionKit(ProceedingJoinPoint point) throws Throwable { HttpSessionHolder.put(super.getHttpServletRequest().getSession()); try { return point.proceed(); } finally { HttpSessionHolder.remove(); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/intercept/SessionTimeoutInterceptor.java ================================================ package cn.enilu.material.admin.core.intercept; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.admin.core.base.controller.BaseController; import org.apache.shiro.session.InvalidSessionException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; /** * 验证session超时的拦截器 * * @author fengshuonan * @date 2017年6月7日21:08:48 */ @Aspect @Component @ConditionalOnProperty(prefix = "apps", name = "session-open", havingValue = "true") public class SessionTimeoutInterceptor extends BaseController { @Pointcut("execution(* cn.enilu.material.admin.*..controller.*.*(..))") public void cutService() { } @Around("cutService()") public Object sessionTimeoutValidate(ProceedingJoinPoint point) throws Throwable { String servletPath = HttpKit.getRequest().getServletPath(); if (servletPath.equals("/kaptcha") || servletPath.equals("/login") || servletPath.equals("/global/sessionError")) { return point.proceed(); }else{ if(ShiroKit.getSession().getAttribute("sessionFlag") == null){ ShiroKit.getSubject().logout(); throw new InvalidSessionException(); }else{ return point.proceed(); } } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/page/PageBT.java ================================================ package cn.enilu.material.admin.core.page; /** * 分页参数类(for BootStrap Table) * * @author fengshuonan * @date 2017年1月21日 下午2:21:35 */ public class PageBT { private int limit; // 每页显示个数 private int offset; // 查询的偏移量(查询的页数 = offset/limit + 1) private String order; // 排序方式 public PageBT() { super(); } public PageBT(int limit, int offset) { super(); this.limit = limit; this.offset = offset; } public int getLimit() { return limit; } public void setLimit(int limit) { this.limit = limit; } public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public String getOrder() { return order; } public void setOrder(String order) { this.order = order; } public int getPageSize() { return this.limit; } public int getPageNumber() { return this.offset / this.limit + 1; } @Override public String toString() { return "PageBT [limit=" + limit + ", offset=" + offset + ", order=" + order + "]"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/page/PageInfoBT.java ================================================ package cn.enilu.material.admin.core.page; import cn.enilu.material.bean.vo.query.Page; import java.util.List; /** * 分页结果的封装(for Bootstrap Table) * * @author fengshuonan * @Date 2017年1月22日 下午11:06:41 */ public class PageInfoBT { // 结果集 private List rows; // 总数 private long total; public PageInfoBT(Page page) { this.rows = page.getRecords(); this.total = page.getTotal(); } public List getRows() { return rows; } public void setRows(List rows) { this.rows = rows; } public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/qr/ImgQrTool.java ================================================ package cn.enilu.material.admin.core.qr; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; /** * 内嵌图片的二维码生成器 * * @author lichunxi */ public class ImgQrTool { private static Logger log = LoggerFactory.getLogger(ImgQrTool.class); // 镶嵌的图片宽度的一般 private static final int IMAGE_WIDTH = 80; private static final int IMAGE_HEIGHT = 80; private static final int IMAGE_HALF_WIDTH = IMAGE_WIDTH / 2; private static final int FRAME_WIDTH = 2; // 二维码写码器 private static MultiFormatWriter mutiWriter = new MultiFormatWriter(); /** * 生成带图片的二维码 * * @param content 二维码的内容 * @param width 宽度 * @param height 高度 * @param srcImagePath 被镶嵌的图片的地址 * @param destImagePath 生成二维码图片的地址 * @author fengshuonan * @since 2.3.0 */ public static void encode(String content, int width, int height, String srcImagePath, String destImagePath) { try { ImageIO.write(genBarcode(content, width, height, srcImagePath), "jpg", new File(destImagePath)); } catch (IOException e) { e.printStackTrace(); } catch (WriterException e) { e.printStackTrace(); } } /** * 生成带图片的二维码 * * @param content 二维码的内容 * @param width 宽度 * @param height 高度 * @param srcImagePath 被镶嵌的图片的地址 * @param destImagePath 生成二维码图片的地址 * @author fengshuonan * @since 2.3.0 */ public static void encode(String content, int width, int height, String srcImagePath, OutputStream outputStream) { try { ImageIO.write(genBarcode(content, width, height, srcImagePath), "jpg", outputStream); } catch (IOException e) { e.printStackTrace(); } catch (WriterException e) { e.printStackTrace(); } } /** * 创建不带参数的二维码 * * @author fengshuonan * @since 2.3.0 */ public static void createSimpleQr(String content, int width, int height, String destImagePath) { FileOutputStream output = null; try { String format = "jpg";// 图像类型 Map hints = new HashMap(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);// 生成矩阵 File dest = new File(destImagePath); output = new FileOutputStream(dest); MatrixToImageWriter.writeToStream(bitMatrix, format, output);// 输出图像 } catch (Exception e) { log.error("生成二维码出错!ImgQrTool:createSimpleQr()", e); } finally { try { output.close(); } catch (IOException e) { log.error("生成二维码出错!ImgQrTool:createSimpleQr()", e); } } } private static BufferedImage genBarcode(String content, int width, int height, String srcImagePath) throws WriterException, IOException { // 读取源图像 BufferedImage scaleImage = scale(srcImagePath, IMAGE_WIDTH, IMAGE_HEIGHT, true); int[][] srcPixels = new int[IMAGE_WIDTH][IMAGE_HEIGHT]; for (int i = 0; i < scaleImage.getWidth(); i++) { for (int j = 0; j < scaleImage.getHeight(); j++) { srcPixels[i][j] = scaleImage.getRGB(i, j); } } Map hint = new HashMap(); hint.put(EncodeHintType.CHARACTER_SET, "utf-8"); hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hint.put(EncodeHintType.MARGIN, 1);// 二维码整体白框 // 生成二维码 BitMatrix matrix = mutiWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint); // 二维矩阵转为一维像素数组 int halfW = matrix.getWidth() / 2; int halfH = matrix.getHeight() / 2; int[] pixels = new int[width * height]; for (int y = 0; y < matrix.getHeight(); y++) { for (int x = 0; x < matrix.getWidth(); x++) { // 读取图片 if (x > halfW - IMAGE_HALF_WIDTH && x < halfW + IMAGE_HALF_WIDTH && y > halfH - IMAGE_HALF_WIDTH && y < halfH + IMAGE_HALF_WIDTH) { pixels[y * width + x] = srcPixels[x - halfW + IMAGE_HALF_WIDTH][y - halfH + IMAGE_HALF_WIDTH]; } // 在图片四周形成边框 else if ((x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW - IMAGE_HALF_WIDTH + FRAME_WIDTH && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH) || (x > halfW + IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH) || (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH && y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH - IMAGE_HALF_WIDTH + FRAME_WIDTH) || (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH && y > halfH + IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH)) { pixels[y * width + x] = 0xfffffff; } else { // 此处可以修改二维码的颜色,可以分别制定二维码和背景的颜色; pixels[y * width + x] = matrix.get(x, y) ? 0xff000000 : 0xfffffff; } } } BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); image.getRaster().setDataElements(0, 0, width, height, pixels); return image; } /** * 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标 * * @param srcImageFile 源文件地址 * @param height 目标高度 * @param width 目标宽度 * @param hasFiller 比例不对时是否需要补白:true为补白; false为不补白; * @throws IOException */ private static BufferedImage scale(String srcImageFile, int height, int width, boolean hasFiller) throws IOException { double ratio = 0.0; // 缩放比例 File file = new File(srcImageFile); BufferedImage srcImage = ImageIO.read(file); Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH); // 计算比例 if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) { if (srcImage.getHeight() > srcImage.getWidth()) { ratio = (new Integer(height)).doubleValue() / srcImage.getHeight(); } else { ratio = (new Integer(width)).doubleValue() / srcImage.getWidth(); } AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null); destImage = op.filter(srcImage, null); } if (hasFiller) {// 补白 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D graphic = image.createGraphics(); graphic.setColor(Color.white); graphic.fillRect(0, 0, width, height); if (width == destImage.getWidth(null)) graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null), destImage.getHeight(null), Color.white, null); else graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null), destImage.getHeight(null), Color.white, null); graphic.dispose(); destImage = image; } return (BufferedImage) destImage; } /** * 生成上方带文字的二维码 */ public static void createQrWithFontsAbove(QrImage para) { try { // 首先生成二维码图片 BufferedImage qrImage = genBarcode(para.getQrContent(), para.getQrWidth(), para.getQrHeight(), para.getQrIconFilePath()); int qrImageWidth = qrImage.getWidth(); int qrImageHeight = qrImage.getHeight(); String[] splitStrLines = null; splitStrLines = splitStrLines(para.getWordContent(), qrImageWidth / para.getWordSize()); int fontsImageHeight = splitStrLines.length * para.getWordSize() + 10; //防止文字遮挡 //创建顶部文字的图片 BufferedImage imageWithFonts = new BufferedImage(qrImageWidth, fontsImageHeight, BufferedImage.TYPE_4BYTE_ABGR); Graphics fontsImageGraphics = imageWithFonts.getGraphics(); fontsImageGraphics.fillRect(0, 0, qrImageWidth, fontsImageHeight); fontsImageGraphics.setColor(Color.black); fontsImageGraphics.setFont(new Font("宋体", Font.PLAIN, para.getWordSize())); //文字长度大于一行的长度,进行分行 if (para.getWordContent().length() > qrImageWidth / para.getWordSize()) {//每行限制的文字个数 for (int i = 0; i < splitStrLines.length; i++) { fontsImageGraphics.drawString(splitStrLines[i], 0, para.getWordSize() * (i + 1)); } } else { fontsImageGraphics.drawString( para.getWordContent(), ((qrImageWidth / para.getWordSize() - para.getWordContent().length()) / 2) * para.getWordSize() + 20, //总长度减去字长度除以2为向右偏移长度 para.getWordSize()); } // 从图片中读取RGB int[] ImageArrayFonts = new int[qrImageWidth * fontsImageHeight]; ImageArrayFonts = imageWithFonts.getRGB(0, 0, qrImageWidth, fontsImageHeight, ImageArrayFonts, 0, qrImageWidth); int[] ImageArrayQr = new int[qrImageWidth * qrImageHeight]; ImageArrayQr = qrImage.getRGB(0, 0, qrImageWidth, qrImageHeight, ImageArrayQr, 0, qrImageWidth); // 生成新图片 BufferedImage ImageNew = new BufferedImage(qrImageWidth, qrImageHeight + fontsImageHeight, BufferedImage.TYPE_INT_RGB); ImageNew.setRGB(0, 0, qrImageWidth, fontsImageHeight, ImageArrayFonts, 0, qrImageWidth);// 设置上半部分的RGB ImageNew.setRGB(0, fontsImageHeight, qrImageWidth, qrImageHeight, ImageArrayQr, 0, qrImageWidth);// 设置下半部分的RGB File outFile = new File(para.getFileOutputPath()); ImageIO.write(ImageNew, "jpg", outFile); } catch (Exception e) { e.printStackTrace(); } } /** * 一行字符串拆分成多行 */ private static String[] splitStrLines(String str, int len) { int blocks = str.length() / len + 1; String[] strs = new String[blocks]; for (int i = 0; i < blocks; i++) { if ((i + 1) * len > str.length()) { strs[i] = str.substring(i * len); } else { strs[i] = str.substring(i * len, (i + 1) * len); } } return strs; } public static void main(String[] args) throws IOException { for (int i = 1; i <= 1; i++) { QrImage para = new QrImage.Builder() .setFileOutputPath("D:\\二维码\\test\\" + i + ".jpg") .setQrContent("http://www.baidu.com?a=" + "123") .setQrHeight(300) .setQrWidth(300) .setQrIconFilePath("D:\\二维码\\中间图标\\1.png") .setTopWrodHeight(100) .setWordContent("test" + 1) .setWordSize(18).build(); ImgQrTool.createQrWithFontsAbove(para); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/qr/MatrixToImageConfig.java ================================================ /* * Copyright 2012 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.qr; import java.awt.image.BufferedImage; /** * Encapsulates custom configuration used in methods of {@link MatrixToImageWriter}. */ public final class MatrixToImageConfig { public static final int BLACK = 0xFF000000; public static final int WHITE = 0xFFFFFFFF; private final int onColor; private final int offColor; /** * Creates a default config with on color {@link #BLACK} and off color {@link #WHITE}, generating normal * black-on-white barcodes. */ public MatrixToImageConfig() { this(BLACK, WHITE); } /** * @param onColor pixel on color, specified as an ARGB value as an int * @param offColor pixel off color, specified as an ARGB value as an int */ public MatrixToImageConfig(int onColor, int offColor) { this.onColor = onColor; this.offColor = offColor; } public int getPixelOnColor() { return onColor; } public int getPixelOffColor() { return offColor; } int getBufferedImageColorModel() { // Use faster BINARY if colors match default return onColor == BLACK && offColor == WHITE ? BufferedImage.TYPE_BYTE_BINARY : BufferedImage.TYPE_INT_RGB; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/qr/MatrixToImageWriter.java ================================================ /* * Copyright 2009 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.qr; import com.google.zxing.common.BitMatrix; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; /** * Writes a {@link BitMatrix} to {@link BufferedImage}, * file or stream. Provided here instead of core since it depends on * Java SE libraries. * * @author Sean Owen */ public final class MatrixToImageWriter { private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig(); private MatrixToImageWriter() {} /** * Renders a {@link BitMatrix} as an image, where "false" bits are rendered * as white, and "true" bits are rendered as black. */ public static BufferedImage toBufferedImage(BitMatrix matrix) { return toBufferedImage(matrix, DEFAULT_CONFIG); } /** * As {@link #toBufferedImage(BitMatrix)}, but allows customization of the output. */ public static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config) { int width = matrix.getWidth(); int height = matrix.getHeight(); BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel()); int onColor = config.getPixelOnColor(); int offColor = config.getPixelOffColor(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor); } } return image; } /** * @deprecated use {@link #writeToPath(BitMatrix, String, Path)} */ @Deprecated public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException { writeToPath(matrix, format, file.toPath()); } /** * Writes a {@link BitMatrix} to a file. * * @see #toBufferedImage(BitMatrix) */ public static void writeToPath(BitMatrix matrix, String format, Path file) throws IOException { writeToPath(matrix, format, file, DEFAULT_CONFIG); } /** * @deprecated use {@link #writeToPath(BitMatrix, String, Path, MatrixToImageConfig)} */ @Deprecated public static void writeToFile(BitMatrix matrix, String format, File file, MatrixToImageConfig config) throws IOException { writeToPath(matrix, format, file.toPath(), config); } /** * As {@link #writeToFile(BitMatrix, String, File)}, but allows customization of the output. */ public static void writeToPath(BitMatrix matrix, String format, Path file, MatrixToImageConfig config) throws IOException { BufferedImage image = toBufferedImage(matrix, config); if (!ImageIO.write(image, format, file.toFile())) { throw new IOException("Could not write an image of format " + format + " to " + file); } } /** * Writes a {@link BitMatrix} to a stream. * * @see #toBufferedImage(BitMatrix) */ public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException { writeToStream(matrix, format, stream, DEFAULT_CONFIG); } /** * As {@link #writeToStream(BitMatrix, String, OutputStream)}, but allows customization of the output. */ public static void writeToStream(BitMatrix matrix, String format, OutputStream stream, MatrixToImageConfig config) throws IOException { BufferedImage image = toBufferedImage(matrix, config); if (!ImageIO.write(image, format, stream)) { throw new IOException("Could not write an image of format " + format); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/qr/QrImage.java ================================================ package cn.enilu.material.admin.core.qr; /** * 二维码图片对象 * * @author fengshuonan * @date 2016年12月8日 上午11:37:09 */ public class QrImage { /** * 二维码的内容 */ private String qrContent; /** * 二维码的宽度 */ private int qrWidth; /** * 二维码的高度 */ private int qrHeight; /** * 二维码中间图标的文件路径 */ private String qrIconFilePath; /** * 二维码中间小图标的边长 */ private int qrIconWidth; /** * 顶部文字的高度 */ private int topWrodHeight; /** * 文字的大小 */ private int wordSize; /** * 文字的内容 */ private String wordContent; /** * 文件的输出路径 */ private String fileOutputPath; public static class Builder { private String qrContent; private int qrWidth; private int qrHeight; private String qrIconFilePath; private int topWrodHeight; private int wordSize; private String wordContent; private String fileOutputPath; private int qrIconWidth; public Builder() { } public Builder setQrContent(String qrContent) { this.qrContent = qrContent; return this; } public Builder setQrWidth(int qrWidth) { this.qrWidth = qrWidth; return this; } public Builder setQrHeight(int qrHeight) { this.qrHeight = qrHeight; return this; } public Builder setQrIconFilePath(String qrIconFilePath) { this.qrIconFilePath = qrIconFilePath; return this; } public Builder setTopWrodHeight(int topWrodHeight) { this.topWrodHeight = topWrodHeight; return this; } public Builder setWordSize(int wordSize) { this.wordSize = wordSize; return this; } public Builder setWordContent(String wordContent) { this.wordContent = wordContent; return this; } public Builder setFileOutputPath(String fileOutputPath) { this.fileOutputPath = fileOutputPath; return this; } public Builder setQrIconWidth(int qrIconWidth) { this.qrIconWidth = qrIconWidth; return this; } public QrImage build() { return new QrImage(this.qrContent, this.qrWidth, this.qrHeight, this.qrIconFilePath, this.qrIconWidth, this.topWrodHeight, this.wordSize, this.wordContent, this.fileOutputPath); } } public QrImage(String qrContent, int qrWidth, int qrHeight, String qrIconFilePath, int qrIconWidth, int topWrodHeight, int wordSize, String wordContent, String fileOutputPath) { super(); this.qrContent = qrContent; this.qrWidth = qrWidth; this.qrHeight = qrHeight; this.qrIconFilePath = qrIconFilePath; this.qrIconWidth = qrIconWidth; this.topWrodHeight = topWrodHeight; this.wordSize = wordSize; this.wordContent = wordContent; this.fileOutputPath = fileOutputPath; } public String getQrContent() { return qrContent; } public int getQrWidth() { return qrWidth; } public int getQrHeight() { return qrHeight; } public String getQrIconFilePath() { return qrIconFilePath; } public int getTopWrodHeight() { return topWrodHeight; } public int getWordSize() { return wordSize; } public String getWordContent() { return wordContent; } public String getFileOutputPath() { return fileOutputPath; } public int getQrIconWidth() { return qrIconWidth; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/BasicType.java ================================================ package cn.enilu.material.admin.core.support; import java.util.HashMap; import java.util.Map; /** * 基本变量类型的枚举 * @author xiaoleilu */ public enum BasicType { BYTE, SHORT, INT, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, CHAR, CHARACTER, STRING; /** 原始类型为Key,包装类型为Value,例如: int.class -> Integer.class. */ public static final Map, Class> wrapperPrimitiveMap = new HashMap, Class>(8); /** 包装类型为Key,原始类型为Value,例如: Integer.class -> int.class. */ public static final Map, Class> primitiveWrapperMap = new HashMap, Class>(8); static { wrapperPrimitiveMap.put(Boolean.class, boolean.class); wrapperPrimitiveMap.put(Byte.class, byte.class); wrapperPrimitiveMap.put(Character.class, char.class); wrapperPrimitiveMap.put(Double.class, double.class); wrapperPrimitiveMap.put(Float.class, float.class); wrapperPrimitiveMap.put(Integer.class, int.class); wrapperPrimitiveMap.put(Long.class, long.class); wrapperPrimitiveMap.put(Short.class, short.class); for (Map.Entry, Class> entry : wrapperPrimitiveMap.entrySet()) { primitiveWrapperMap.put(entry.getValue(), entry.getKey()); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/BeanKit.java ================================================ package cn.enilu.material.admin.core.support; import cn.enilu.material.admin.core.support.exception.ToolBoxException; import cn.enilu.material.utils.Convert; import cn.enilu.material.utils.StrKit; import java.beans.*; import java.lang.reflect.Method; import java.util.*; import java.util.Map.Entry; /** * Bean工具类 * * @author Looly */ public class BeanKit { /** * 判断是否为Bean对象 * * @param clazz 待测试类 * @return 是否为Bean对象 */ public static boolean isBean(Class clazz) { if (ClassKit.isNormalClass(clazz)) { Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) { //检测包含标准的setXXX方法即视为标准的JavaBean return true; } } } return false; } public static PropertyEditor findEditor(Class type) { return PropertyEditorManager.findEditor(type); } /** * 获得Bean字段描述数组 * * @param clazz Bean类 * @return 字段描述数组 * @throws IntrospectionException */ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws IntrospectionException { return Introspector.getBeanInfo(clazz).getPropertyDescriptors(); } /** * 获得字段名和字段描述Map * * @param clazz Bean类 * @return 字段名和字段描述Map * @throws IntrospectionException */ public static Map getFieldNamePropertyDescriptorMap(Class clazz) throws IntrospectionException { final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz); Map map = new HashMap<>(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { map.put(propertyDescriptor.getName(), propertyDescriptor); } return map; } /** * 获得Bean类属性描述 * * @param clazz Bean类 * @param fieldName 字段名 * @return PropertyDescriptor * @throws IntrospectionException */ public static PropertyDescriptor getPropertyDescriptor(Class clazz, final String fieldName) throws IntrospectionException { PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (ObjectKit.equals(fieldName, propertyDescriptor.getName())) { return propertyDescriptor; } } return null; } /** * Map转换为Bean对象 * * @param map Map * @param beanClass Bean Class * @return Bean */ public static T mapToBean(Map map, Class beanClass) { return fillBeanWithMap(map, ClassKit.newInstance(beanClass)); } /** * Map转换为Bean对象
    * 忽略大小写 * * @param map Map * @param beanClass Bean Class * @return Bean */ public static T mapToBeanIgnoreCase(Map map, Class beanClass) { return fillBeanWithMapIgnoreCase(map, ClassKit.newInstance(beanClass)); } /** * 使用Map填充Bean对象 * * @param map Map * @param bean Bean * @return Bean */ public static T fillBeanWithMap(final Map map, T bean) { return fillBean(bean, new ValueProvider() { @Override public Object value(String name) { return map.get(name); } }); } /** * 使用Map填充Bean对象,可配置将下划线转换为驼峰 * * @param map Map * @param bean Bean * @param isToCamelCase 是否将下划线模式转换为驼峰模式 * @return Bean */ public static T fillBeanWithMap(Map map, T bean, boolean isToCamelCase) { if (isToCamelCase) { final Map map2 = new HashMap(); for (Entry entry : map.entrySet()) { final Object key = entry.getKey(); if (null != key && key instanceof String) { final String keyStr = (String) key; map2.put(StrKit.toCamelCase(keyStr), entry.getValue()); } else { map2.put(key, entry.getValue()); } } return fillBeanWithMap(map2, bean); } return fillBeanWithMap(map, bean); } /** * 使用Map填充Bean对象,忽略大小写 * * @param map Map * @param bean Bean * @return Bean */ public static T fillBeanWithMapIgnoreCase(Map map, T bean) { final Map map2 = new HashMap(); for (Entry entry : map.entrySet()) { final Object key = entry.getKey(); if (key instanceof String) { final String keyStr = (String) key; map2.put(keyStr.toLowerCase(), entry.getValue()); } else { map2.put(key, entry.getValue()); } } return fillBean(bean, new ValueProvider() { @Override public Object value(String name) { return map2.get(name.toLowerCase()); } }); } /** * ServletRequest 参数转Bean * * @param request ServletRequest * @param beanClass Bean Class * @return Bean */ public static T requestParamToBean(javax.servlet.ServletRequest request, Class beanClass) { return fillBeanWithRequestParam(request, ClassKit.newInstance(beanClass)); } /** * ServletRequest 参数转Bean * * @param request ServletRequest * @param bean Bean * @return Bean */ public static T fillBeanWithRequestParam(final javax.servlet.ServletRequest request, T bean) { final String beanName = StrKit.lowerFirst(bean.getClass().getSimpleName()); return fillBean(bean, new ValueProvider() { @Override public Object value(String name) { String value = request.getParameter(name); if (StrKit.isEmpty(value)) { // 使用类名前缀尝试查找值 value = request.getParameter(beanName + StrKit.DOT + name); if (StrKit.isEmpty(value)) { // 此处取得的值为空时跳过,包括null和"" value = null; } } return value; } }); } /** * ServletRequest 参数转Bean * * @param * @param beanClass Bean Class * @param valueProvider 值提供者 * @return Bean */ public static T toBean(Class beanClass, ValueProvider valueProvider) { return fillBean(ClassKit.newInstance(beanClass), valueProvider); } /** * 填充Bean * * @param * @param bean Bean * @param valueProvider 值提供者 * @return Bean */ public static T fillBean(T bean, ValueProvider valueProvider) { if (null == valueProvider) { return bean; } Class beanClass = bean.getClass(); try { PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(beanClass); String propertyName; Object value; for (PropertyDescriptor property : propertyDescriptors) { propertyName = property.getName(); value = valueProvider.value(propertyName); if (null == value) { continue; } try { property.getWriteMethod().invoke(bean, Convert.parse(property.getPropertyType(), value)); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { throw new ToolBoxException(e); } return bean; } /** * 对象转Map * * @param bean bean对象 * @return Map */ public static Map beanToMap(T bean) { return beanToMap(bean, false); } /** * 对象转Map * * @param bean bean对象 * @return Map */ public static List> listToMapList(List bean) { ArrayList> maps = new ArrayList<>(); for (T t : bean) { maps.add(beanToMap(bean, false)); } return maps; } /** * 对象转Map * * @param bean bean对象 * @param isToUnderlineCase 是否转换为下划线模式 * @return Map */ public static Map beanToMap(T bean, boolean isToUnderlineCase) { if (bean == null) { return null; } Map map = new HashMap(); try { final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(bean.getClass()); for (PropertyDescriptor property : propertyDescriptors) { String key = property.getName(); // 过滤class属性 if (false == key.equals("class")) { // 得到property对应的getter方法 Method getter = property.getReadMethod(); Object value = getter.invoke(bean); if (null != value) { map.put(isToUnderlineCase ? StrKit.toUnderlineCase(key) : key, value); } } } } catch (Exception e) { throw new ToolBoxException(e); } return map; } /** * 复制Bean对象属性 * * @param source 源Bean对象 * @param target 目标Bean对象 */ public static void copyProperties(Object source, Object target) { copyProperties(source, target, CopyOptions.create()); } /** * 复制Bean对象属性
    * 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 * * @param source 源Bean对象 * @param target 目标Bean对象 * @param ignoreProperties 不拷贝的的属性列表 */ public static void copyProperties(Object source, Object target, String... ignoreProperties) { copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties)); } /** * 复制Bean对象属性
    * 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 * * @param source 源Bean对象 * @param target 目标Bean对象 * @param copyOptions 拷贝选项,见 {@link CopyOptions} */ public static void copyProperties(Object source, Object target, CopyOptions copyOptions) { if (null == copyOptions) { copyOptions = new CopyOptions(); } Class actualEditable = target.getClass(); if (copyOptions.editable != null) { //检查限制类是否为target的父类或接口 if (!copyOptions.editable.isInstance(target)) { throw new IllegalArgumentException(StrKit.format("Target class [{}] not assignable to Editable class [{}]", target.getClass().getName(), copyOptions.editable.getName())); } actualEditable = copyOptions.editable; } PropertyDescriptor[] targetPds = null; Map sourcePdMap; try { sourcePdMap = getFieldNamePropertyDescriptorMap(source.getClass()); targetPds = getPropertyDescriptors(actualEditable); } catch (IntrospectionException e) { throw new ToolBoxException(e); } HashSet ignoreSet = copyOptions.ignoreProperties != null ? CollectionKit.newHashSet(copyOptions.ignoreProperties) : null; for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreSet == null || false == ignoreSet.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = sourcePdMap.get(targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); // 源对象字段的getter方法返回值必须可转换为目标对象setter方法的第一个参数 if (readMethod != null && ClassKit.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { Object value = ClassKit.setAccessible(readMethod).invoke(source); if (null != value || false == copyOptions.isIgnoreNullValue) { ClassKit.setAccessible(writeMethod).invoke(target, value); } } catch (Throwable ex) { throw new ToolBoxException(ex, "Copy property [{}] to [{}] error: {}", sourcePd.getName(), targetPd.getName(), ex.getMessage()); } } } } } } /** * 值提供者,用于提供Bean注入时参数对应值得抽象接口
    * 继承或匿名实例化此接口
    * 在Bean注入过程中,Bean获得字段名,通过外部方式根据这个字段名查找相应的字段值,然后注入Bean
    * * @author Looly */ public static interface ValueProvider { /** * 获取值 * * @param name Bean对象中参数名 * @return 对应参数名的值 */ public Object value(String name); } /** * 属性拷贝选项
    * 包括:
    * 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
    * 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
    * 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值
    * * @author Looly */ public static class CopyOptions { /** * 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 */ private Class editable; /** * 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null */ private boolean isIgnoreNullValue; /** * 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 */ private String[] ignoreProperties; /** * 创建拷贝选项 * * @return 拷贝选项 */ public static CopyOptions create() { return new CopyOptions(); } /** * 创建拷贝选项 * * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 * @param isIgnoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 * @return 拷贝选项 */ public static CopyOptions create(Class editable, boolean isIgnoreNullValue, String... ignoreProperties) { return new CopyOptions(editable, isIgnoreNullValue, ignoreProperties); } /** * 构造拷贝选项 */ public CopyOptions() { } /** * 构造拷贝选项 * * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 * @param isIgnoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 */ public CopyOptions(Class editable, boolean isIgnoreNullValue, String... ignoreProperties) { this.editable = editable; this.isIgnoreNullValue = isIgnoreNullValue; this.ignoreProperties = ignoreProperties; } /** * 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性 * * @param editable 限制的类或接口 * @return CopyOptions */ public CopyOptions setEditable(Class editable) { this.editable = editable; return this; } /** * 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * * @param isIgnoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null * @return CopyOptions */ public CopyOptions setIgnoreNullValue(boolean isIgnoreNullVall) { this.isIgnoreNullValue = isIgnoreNullVall; return this; } /** * 设置忽略的属性列表,设置一个属性列表,不拷贝这些属性值 * * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值 * @return CopyOptions */ public CopyOptions setIgnoreProperties(String... ignoreProperties) { this.ignoreProperties = ignoreProperties; return this; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/ClassKit.java ================================================ package cn.enilu.material.admin.core.support; import cn.enilu.material.admin.core.support.exception.ToolBoxException; import cn.enilu.material.utils.StrKit; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * 类工具类 * 1、扫描指定包下的所有类
    * 参考 http://www.oschina.net/code/snippet_234657_22722 * @author seaside_hi, xiaoleilu, chill * */ public class ClassKit { private ClassKit() { // 静态类不可实例化 } /** * 是否为标准的类
    * 这个类必须:
    * 1、非接口 2、非抽象类 3、非Enum枚举 4、非数组 5、非注解 6、非原始类型(int, long等) * * @param clazz 类 * @return 是否为标准类 */ public static boolean isNormalClass(Class clazz) { return null != clazz && false == clazz.isInterface() && false == isAbstract(clazz) && false == clazz.isEnum() && false == clazz.isArray() && false == clazz.isAnnotation() && false == clazz .isSynthetic() && false == clazz.isPrimitive(); } /** * 是否为抽象类 * * @param clazz 类 * @return 是否为抽象类 */ public static boolean isAbstract(Class clazz) { return Modifier.isAbstract(clazz.getModifiers()); } /** * 实例化对象 * * @param clazz 类名 * @return 对象 */ @SuppressWarnings("unchecked") public static T newInstance(String clazz) { if (null == clazz) return null; try { return (T) Class.forName(clazz).newInstance(); } catch (Exception e) { throw new ToolBoxException(StrKit.format("Instance class [{}] error!", clazz), e); } } /** * 实例化对象 * * @param clazz 类 * @return 对象 */ public static T newInstance(Class clazz) { if (null == clazz) return null; try { return (T) clazz.newInstance(); } catch (Exception e) { throw new ToolBoxException(StrKit.format("Instance class [{}] error!", clazz), e); } } /** * 实例化对象 * * @param clazz 类 * @return 对象 */ public static T newInstance(Class clazz, Object... params) { if (null == clazz) return null; if (CollectionKit.isEmpty(params)) { return newInstance(clazz); } try { return clazz.getDeclaredConstructor(getClasses(params)).newInstance(params); } catch (Exception e) { throw new ToolBoxException(StrKit.format("Instance class [{}] error!", clazz), e); } } /** * 获得对象数组的类数组 * @param objects 对象数组 * @return 类数组 */ public static Class[] getClasses(Object... objects){ Class[] classes = new Class[objects.length]; for (int i = 0; i < objects.length; i++) { classes[i] = objects[i].getClass(); } return classes; } /** * 检查目标类是否可以从原类转化
    * 转化包括:
    * 1、原类是对象,目标类型是原类型实现的接口
    * 2、目标类型是原类型的父类
    * 3、两者是原始类型或者包装类型(相互转换) * * @param targetType 目标类型 * @param sourceType 原类型 * @return 是否可转化 */ public static boolean isAssignable(Class targetType, Class sourceType) { if (null == targetType || null == sourceType) { return false; } // 对象类型 if (targetType.isAssignableFrom(sourceType)) { return true; } // 基本类型 if (targetType.isPrimitive()) { // 原始类型 Class resolvedPrimitive = BasicType.wrapperPrimitiveMap.get(sourceType); if (resolvedPrimitive != null && targetType.equals(resolvedPrimitive)) { return true; } } else { // 包装类型 Class resolvedWrapper = BasicType.primitiveWrapperMap.get(sourceType); if (resolvedWrapper != null && targetType.isAssignableFrom(resolvedWrapper)) { return true; } } return false; } /** * 设置方法为可访问 * * @param method 方法 * @return 方法 */ public static Method setAccessible(Method method) { if (null != method && ClassKit.isNotPublic(method)) { method.setAccessible(true); } return method; } /** * 指定类是否为非public * * @param clazz 类 * @return 是否为非public */ public static boolean isNotPublic(Class clazz) { return false == isPublic(clazz); } /** * 指定方法是否为非public * * @param method 方法 * @return 是否为非public */ public static boolean isNotPublic(Method method) { return false == isPublic(method); } /** * 指定类是否为Public * * @param clazz 类 * @return 是否为public */ public static boolean isPublic(Class clazz) { if (null == clazz) { throw new NullPointerException("Class to provided is null."); } return Modifier.isPublic(clazz.getModifiers()); } /** * 指定方法是否为Public * * @param method 方法 * @return 是否为public */ public static boolean isPublic(Method method) { if (null == method) { throw new NullPointerException("Method to provided is null."); } return isPublic(method.getDeclaringClass()); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/CollectionKit.java ================================================ package cn.enilu.material.admin.core.support; import cn.enilu.material.admin.core.support.exception.ToolBoxException; import cn.enilu.material.utils.StrKit; import java.lang.reflect.Array; import java.util.*; import java.util.Map.Entry; /** * 集合相关工具类,包括数组 * * @author xiaoleilu * */ public class CollectionKit { private CollectionKit() { // 静态类不可实例化 } /** * 以 conjunction 为分隔符将集合转换为字符串 * * @param 被处理的集合 * @param collection 集合 * @param conjunction 分隔符 * @return 连接后的字符串 */ public static String join(Iterable collection, String conjunction) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (T item : collection) { if (isFirst) { isFirst = false; } else { sb.append(conjunction); } sb.append(item); } return sb.toString(); } /** * 以 conjunction 为分隔符将数组转换为字符串 * * @param 被处理的集合 * @param array 数组 * @param conjunction 分隔符 * @return 连接后的字符串 */ public static String join(T[] array, String conjunction) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (T item : array) { if (isFirst) { isFirst = false; } else { sb.append(conjunction); } sb.append(item); } return sb.toString(); } /** * 将多个集合排序并显示不同的段落(分页) * @param pageNo 页码 * @param numPerPage 每页的条目数 * @param comparator 比较器 * @param colls 集合数组 * @return 分页后的段落内容 */ @SafeVarargs public static List sortPageAll(int pageNo, int numPerPage, Comparator comparator, Collection... colls) { final List result = new ArrayList(); for (Collection coll : colls) { result.addAll(coll); } Collections.sort(result, comparator); //第一页且数目少于第一页显示的数目 if(pageNo <=1 && result.size() <= numPerPage) { return result; } final int[] startEnd = PageKit.transToStartEnd(pageNo, numPerPage); return result.subList(startEnd[0], startEnd[1]); } /** * 将多个集合排序并显示不同的段落(分页) * @param pageNo 页码 * @param numPerPage 每页的条目数 * @param comparator 比较器 * @param colls 集合数组 * @return 分业后的段落内容 */ // @SafeVarargs // public static List sortPageAll2(int pageNo, int numPerPage, Comparator comparator, Collection... colls) { // BoundedPriorityQueue queue = new BoundedPriorityQueue(pageNo * numPerPage); // for (Collection coll : colls) { // queue.addAll(coll); // } // // //第一页且数目少于第一页显示的数目 // if(pageNo <=1 && queue.size() <= numPerPage) { // return queue.toList(); // } // // final int[] startEnd = PageKit.transToStartEnd(pageNo, numPerPage); // return queue.toList().subList(startEnd[0], startEnd[1]); // } /** * 将Set排序(根据Entry的值) * * @param set 被排序的Set * @return 排序后的Set */ public static List> sortEntrySetToList(Set> set) { List> list = new LinkedList>(set); Collections.sort(list, new Comparator>(){ @Override public int compare(Entry o1, Entry o2) { if (o1.getValue() > o2.getValue()){ return 1; } if (o1.getValue() < o2.getValue()){ return -1; } return 0; } }); return list; } /** * 切取部分数据 * * @param 集合元素类型 * @param surplusAlaDatas 原数据 * @param partSize 每部分数据的长度 * @return 切取出的数据或null */ public static List popPart(Stack surplusAlaDatas, int partSize) { if (surplusAlaDatas == null || surplusAlaDatas.size() <= 0){ return null; } final List currentAlaDatas = new ArrayList(); int size = surplusAlaDatas.size(); // 切割 if (size > partSize) { for (int i = 0; i < partSize; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } else { for (int i = 0; i < size; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } return currentAlaDatas; } /** * 切取部分数据 * * @param 集合元素类型 * @param surplusAlaDatas 原数据 * @param partSize 每部分数据的长度 * @return 切取出的数据或null */ public static List popPart(Deque surplusAlaDatas, int partSize) { if (surplusAlaDatas == null || surplusAlaDatas.size() <= 0){ return null; } final List currentAlaDatas = new ArrayList(); int size = surplusAlaDatas.size(); // 切割 if (size > partSize) { for (int i = 0; i < partSize; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } else { for (int i = 0; i < size; i++) { currentAlaDatas.add(surplusAlaDatas.pop()); } } return currentAlaDatas; } /** * 新建一个HashMap * * @return HashMap对象 */ public static HashMap newHashMap() { return new HashMap(); } /** * 新建一个HashMap * @param size 初始大小,由于默认负载因子0.75,传入的size会实际初始大小为size / 0.75 * @return HashMap对象 */ public static HashMap newHashMap(int size) { return new HashMap((int)(size / 0.75)); } /** * 新建一个HashSet * * @return HashSet对象 */ public static HashSet newHashSet() { return new HashSet(); } /** * 新建一个HashSet * * @return HashSet对象 */ @SafeVarargs public static HashSet newHashSet(T... ts) { HashSet set = new HashSet(); for (T t : ts) { set.add(t); } return set; } /** * 新建一个ArrayList * * @return ArrayList对象 */ public static ArrayList newArrayList() { return new ArrayList(); } /** * 新建一个ArrayList * * @return ArrayList对象 */ @SafeVarargs public static ArrayList newArrayList(T... values) { return new ArrayList(Arrays.asList(values)); } /** * 将新元素添加到已有数组中
    * 添加新元素会生成一个新的数组,不影响原数组 * * @param buffer 已有数组 * @param newElement 新元素 * @return 新数组 */ public static T[] append(T[] buffer, T newElement) { T[] t = resize(buffer, buffer.length + 1, newElement.getClass()); t[buffer.length] = newElement; return t; } /** * 生成一个新的重新设置大小的数组 * * @param buffer 原数组 * @param newSize 新的数组大小 * @param componentType 数组元素类型 * @return 调整后的新数组 */ public static T[] resize(T[] buffer, int newSize, Class componentType) { T[] newArray = newArray(componentType, newSize); System.arraycopy(buffer, 0, newArray, 0, buffer.length >= newSize ? newSize : buffer.length); return newArray; } /** * 新建一个空数组 * @param componentType 元素类型 * @param newSize 大小 * @return 空数组 */ @SuppressWarnings("unchecked") public static T[] newArray(Class componentType, int newSize) { return (T[]) Array.newInstance(componentType, newSize); } /** * 生成一个新的重新设置大小的数组
    * 新数组的类型为原数组的类型 * * @param buffer 原数组 * @param newSize 新的数组大小 * @return 调整后的新数组 */ public static T[] resize(T[] buffer, int newSize) { return resize(buffer, newSize, buffer.getClass().getComponentType()); } /** * 将多个数组合并在一起
    * 忽略null的数组 * * @param arrays 数组集合 * @return 合并后的数组 */ @SafeVarargs public static T[] addAll(T[]... arrays) { if (arrays.length == 1) { return arrays[0]; } int length = 0; for (T[] array : arrays) { if(array == null) { continue; } length += array.length; } T[] result = newArray(arrays.getClass().getComponentType().getComponentType(), length); length = 0; for (T[] array : arrays) { if(array == null) { continue; } System.arraycopy(array, 0, result, length, array.length); length += array.length; } return result; } /** * 克隆数组 * @param array 被克隆的数组 * @return 新数组 */ public static T[] clone(T[] array) { if (array == null) { return null; } return array.clone(); } /** * 生成一个数字列表
    * 自动判定正序反序 * @param excludedEnd 结束的数字(不包含) * @return 数字列表 */ public static int[] range(int excludedEnd) { return range(0, excludedEnd, 1); } /** * 生成一个数字列表
    * 自动判定正序反序 * @param includedStart 开始的数字(包含) * @param excludedEnd 结束的数字(不包含) * @return 数字列表 */ public static int[] range(int includedStart, int excludedEnd) { return range(includedStart, excludedEnd, 1); } /** * 生成一个数字列表
    * 自动判定正序反序 * @param includedStart 开始的数字(包含) * @param excludedEnd 结束的数字(不包含) * @param step 步进 * @return 数字列表 */ public static int[] range(int includedStart, int excludedEnd, int step) { if(includedStart > excludedEnd) { int tmp = includedStart; includedStart = excludedEnd; excludedEnd = tmp; } if(step <=0) { step = 1; } int deviation = excludedEnd - includedStart; int length = deviation / step; if(deviation % step != 0) { length += 1; } int[] range = new int[length]; for(int i = 0; i < length; i++) { range[i] = includedStart; includedStart += step; } return range; } /** * 截取数组的部分 * @param list 被截取的数组 * @param start 开始位置(包含) * @param end 结束位置(不包含) * @return 截取后的数组,当开始位置超过最大时,返回null */ public static List sub(List list, int start, int end) { if(list == null || list.isEmpty()) { return null; } if(start < 0) { start = 0; } if(end < 0) { end = 0; } if(start > end) { int tmp = start; start = end; end = tmp; } final int size = list.size(); if(end > size) { if(start >= size) { return null; } end = size; } return list.subList(start, end); } /** * 截取集合的部分 * @param list 被截取的数组 * @param start 开始位置(包含) * @param end 结束位置(不包含) * @return 截取后的数组,当开始位置超过最大时,返回null */ public static List sub(Collection list, int start, int end) { if(list == null || list.isEmpty()) { return null; } return sub(new ArrayList(list), start, end); } /** * 数组是否为空 * @param array 数组 * @return 是否为空 */ public static boolean isEmpty(T[] array) { return array == null || array.length == 0; } /** * 数组是否为非空 * @param array 数组 * @return 是否为非空 */ public static boolean isNotEmpty(T[] array) { return false == isEmpty(array); } /** * 集合是否为空 * @param collection 集合 * @return 是否为空 */ public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /** * 集合是否为非空 * @param collection 集合 * @return 是否为非空 */ public static boolean isNotEmpty(Collection collection) { return false == isEmpty(collection); } /** * Map是否为空 * @param map 集合 * @return 是否为空 */ public static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } /** * Map是否为非空 * @param map 集合 * @return 是否为非空 */ public static boolean isNotEmpty(Map map) { return false == isEmpty(map); } /** * 映射键值(参考Python的zip()函数)
    * 例如:
    * keys = [a,b,c,d]
    * values = [1,2,3,4]
    * 则得到的Map是 {a=1, b=2, c=3, d=4}
    * 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(T[] keys, K[] values) { if(isEmpty(keys) || isEmpty(values)) { return null; } final int size = Math.min(keys.length, values.length); final Map map = new HashMap((int)(size / 0.75)); for(int i = 0; i < size; i++) { map.put(keys[i], values[i]); } return map; } /** * 映射键值(参考Python的zip()函数)
    * 例如:
    * keys = a,b,c,d
    * values = 1,2,3,4
    * delimiter = , * 则得到的Map是 {a=1, b=2, c=3, d=4}
    * 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(String keys, String values, String delimiter) { return zip(StrKit.split(keys, delimiter), StrKit.split(values, delimiter)); } /** * 映射键值(参考Python的zip()函数)
    * 例如:
    * keys = [a,b,c,d]
    * values = [1,2,3,4]
    * 则得到的Map是 {a=1, b=2, c=3, d=4}
    * 如果两个数组长度不同,则只对应最短部分 * @param keys 键列表 * @param values 值列表 * @return Map */ public static Map zip(Collection keys, Collection values) { if(isEmpty(keys) || isEmpty(values)) { return null; } final List keyList = new ArrayList(keys); final List valueList = new ArrayList(values); final int size = Math.min(keys.size(), values.size()); final Map map = new HashMap((int)(size / 0.75)); for(int i = 0; i < size; i++) { map.put(keyList.get(i), valueList.get(i)); } return map; } /** * 数组中是否包含元素 * @param array 数组 * @param value 被检查的元素 * @return 是否包含 */ public static boolean contains(T[] array, T value) { final Class componetType = array.getClass().getComponentType(); boolean isPrimitive = false; if(null != componetType) { isPrimitive = componetType.isPrimitive(); } for (T t : array) { if(t == value) { return true; }else if(false == isPrimitive && null != value && value.equals(t)) { return true; } } return false; } /** * 将Entry集合转换为HashMap * @param entryCollection entry集合 * @return Map */ public static HashMap toMap(Collection> entryCollection) { HashMap map = new HashMap(); for (Entry entry : entryCollection) { map.put(entry.getKey(), entry.getValue()); } return map; } /** * 将集合转换为排序后的TreeSet * @param collection 集合 * @param comparator 比较器 * @return treeSet */ public static TreeSet toTreeSet(Collection collection, Comparator comparator){ final TreeSet treeSet = new TreeSet(comparator); for (T t : collection) { treeSet.add(t); } return treeSet; } /** * 排序集合 * @param collection 集合 * @param comparator 比较器 * @return treeSet */ public static List sort(Collection collection, Comparator comparator){ List list = new ArrayList(collection); Collections.sort(list, comparator); return list; } //------------------------------------------------------------------- 基本类型的数组转换为包装类型数组 /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Integer[] wrap(int... values){ final int length = values.length; Integer[] array = new Integer[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Long[] wrap(long... values){ final int length = values.length; Long[] array = new Long[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Character[] wrap(char... values){ final int length = values.length; Character[] array = new Character[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Byte[] wrap(byte... values){ final int length = values.length; Byte[] array = new Byte[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Short[] wrap(short... values){ final int length = values.length; Short[] array = new Short[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Float[] wrap(float... values){ final int length = values.length; Float[] array = new Float[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Double[] wrap(double... values){ final int length = values.length; Double[] array = new Double[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 将基本类型数组包装为包装类型 * @param values 基本类型数组 * @return 包装类型数组 */ public static Boolean[] wrap(boolean... values){ final int length = values.length; Boolean[] array = new Boolean[length]; for(int i = 0; i < length; i++){ array[i] = values[i]; } return array; } /** * 判定给定对象是否为数组类型 * @param obj 对象 * @return 是否为数组类型 */ public static boolean isArray(Object obj){ return obj.getClass().isArray(); } /** * 数组或集合转String * * @param obj 集合或数组对象 * @return 数组字符串,与集合转字符串格式相同 */ public static String toString(Object obj) { if (null == obj) { return null; } if (isArray(obj)) { try { return Arrays.deepToString((Object[]) obj); } catch (Exception e) { final String className = obj.getClass().getComponentType().getName(); switch (className) { case "long": return Arrays.toString((long[]) obj); case "int": return Arrays.toString((int[]) obj); case "short": return Arrays.toString((short[]) obj); case "char": return Arrays.toString((char[]) obj); case "byte": return Arrays.toString((byte[]) obj); case "boolean": return Arrays.toString((boolean[]) obj); case "float": return Arrays.toString((float[]) obj); case "double": return Arrays.toString((double[]) obj); default: throw new ToolBoxException(e); } } } return obj.toString(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/DateTime.java ================================================ package cn.enilu.material.admin.core.support; import java.util.Date; /** * 封装java.util.Date * @author xiaoleilu * */ public class DateTime extends Date{ private static final long serialVersionUID = -5395712593979185936L; /** * 转换JDK date为 DateTime * @param date JDK Date * @return DateTime */ public static DateTime parse(Date date) { return new DateTime(date); } /** * 当前时间 */ public DateTime() { super(); } /** * 给定日期的构造 * @param date 日期 */ public DateTime(Date date) { this(date.getTime()); } /** * 给定日期毫秒数的构造 * @param timeMillis 日期毫秒数 */ public DateTime(long timeMillis) { super(timeMillis); } @Override public String toString() { return DateTimeKit.formatDateTime(this); } public String toString(String format) { return DateTimeKit.format(this, format); } /** * @return 输出精确到毫秒的标准日期形式 */ public String toMsStr() { return DateTimeKit.format(this, DateTimeKit.NORM_DATETIME_MS_PATTERN); } /** * @return java.util.Date */ public Date toDate() { return new Date(this.getTime()); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/DateTimeKit.java ================================================ package cn.enilu.material.admin.core.support; import cn.enilu.material.admin.core.support.exception.ToolBoxException; import cn.enilu.material.utils.StrKit; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashSet; import java.util.Locale; /** * 时间工具类 * @author xiaoleilu */ public class DateTimeKit { /** 毫秒 */ public final static long MS = 1; /** 每秒钟的毫秒数 */ public final static long SECOND_MS = MS * 1000; /** 每分钟的毫秒数 */ public final static long MINUTE_MS = SECOND_MS * 60; /** 每小时的毫秒数 */ public final static long HOUR_MS = MINUTE_MS * 60; /** 每天的毫秒数 */ public final static long DAY_MS = HOUR_MS * 24; /** 标准日期格式 */ public final static String NORM_DATE_PATTERN = "yyyy-MM-dd"; /** 标准时间格式 */ public final static String NORM_TIME_PATTERN = "HH:mm:ss"; /** 标准日期时间格式,精确到分 */ public final static String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm"; /** 标准日期时间格式,精确到秒 */ public final static String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; /** 标准日期时间格式,精确到毫秒 */ public final static String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; /** HTTP头中日期时间格式 */ public final static String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; /** 标准日期(不含时间)格式化器 */ // private final static SimpleDateFormat NORM_DATE_FORMAT = new SimpleDateFormat(NORM_DATE_PATTERN); private static ThreadLocal NORM_DATE_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_DATE_PATTERN); }; }; /** 标准时间格式化器 */ // private final static SimpleDateFormat NORM_TIME_FORMAT = new SimpleDateFormat(NORM_TIME_PATTERN); private static ThreadLocal NORM_TIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_TIME_PATTERN); }; }; /** 标准日期时间格式化器 */ // private final static SimpleDateFormat NORM_DATETIME_FORMAT = new SimpleDateFormat(NORM_DATETIME_PATTERN); private static ThreadLocal NORM_DATETIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(NORM_DATETIME_PATTERN); }; }; /** HTTP日期时间格式化器 */ // private final static SimpleDateFormat HTTP_DATETIME_FORMAT = new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US); private static ThreadLocal HTTP_DATETIME_FORMAT = new ThreadLocal(){ synchronized protected SimpleDateFormat initialValue() { return new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US); }; }; /** * 当前时间,格式 yyyy-MM-dd HH:mm:ss * * @return 当前时间的标准形式字符串 */ public static String now() { return formatDateTime(new DateTime()); } /** * 当前时间long * * @param isNano 是否为高精度时间 * @return 时间 */ public static long current(boolean isNano) { return isNano ? System.nanoTime() : System.currentTimeMillis(); } /** * 当前日期,格式 yyyy-MM-dd * * @return 当前日期的标准形式字符串 */ public static String today() { return formatDate(new DateTime()); } /** * @return 当前月份 */ public static int thisMonth() { return month(date()); } /** * @return 今年 */ public static int thisYear() { return year(date()); } /** * @return 当前时间 */ public static DateTime date() { return new DateTime(); } /** * Long类型时间转为Date * * @param date Long类型Date(Unix时间戳) * @return 时间对象 */ public static DateTime date(long date) { return new DateTime(date); } /** * 转换为Calendar对象 * * @param date 日期对象 * @return Calendar对象 */ public static Calendar toCalendar(Date date) { final Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal; } /** * 获得月份,从1月开始计数 * * @param date 日期 * @return 月份 */ public static int month(Date date) { return toCalendar(date).get(Calendar.MONTH) + 1; } /** * 获得年 * * @param date 日期 * @return 年 */ public static int year(Date date) { return toCalendar(date).get(Calendar.YEAR); } /** * 获得季节 * * @param date 日期 * @return 第几个季节 */ public static int season(Date date) { return toCalendar(date).get(Calendar.MONTH) / 3 + 1; } /** * 获得指定日期年份和季节
    * 格式:[20131]表示2013年第一季度 * * @param date 日期 * @return Season ,类似于 20132 */ public static String yearAndSeason(Date date) { return yearAndSeason(toCalendar(date)); } /** * 获得指定日期区间内的年份和季节
    * * @param startDate 其实日期(包含) * @param endDate 结束日期(包含) * @return Season列表 ,元素类似于 20132 */ public static LinkedHashSet yearAndSeasons(Date startDate, Date endDate) { final LinkedHashSet seasons = new LinkedHashSet(); if (startDate == null || endDate == null) { return seasons; } final Calendar cal = Calendar.getInstance(); cal.setTime(startDate); while (true) { // 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环 if (startDate.after(endDate)) { startDate = endDate; } seasons.add(yearAndSeason(cal)); if (startDate.equals(endDate)) { break; } cal.add(Calendar.MONTH, 3); startDate = cal.getTime(); } return seasons; } // ------------------------------------ Format start ---------------------------------------------- /** * 根据特定格式格式化日期 * * @param date 被格式化的日期 * @param format 格式 * @return 格式化后的字符串 */ public static String format(Date date, String format) { return new SimpleDateFormat(format).format(date); } /** * 格式 yyyy-MM-dd HH:mm:ss * * @param date 被格式化的日期 * @return 格式化后的日期 */ public static String formatDateTime(Date date) { if(null == date){ return null; } return NORM_DATETIME_FORMAT.get().format(date); } /** * 格式 yyyy-MM-dd * * @param date 被格式化的日期 * @return 格式化后的字符串 */ public static String formatDate(Date date) { if(null == date){ return null; } return NORM_DATE_FORMAT.get().format(date); } /** * 格式化为Http的标准日期格式 * * @param date 被格式化的日期 * @return HTTP标准形式日期字符串 */ public static String formatHttpDate(Date date) { if(null == date){ return null; } return HTTP_DATETIME_FORMAT.get().format(date); } // ------------------------------------ Format end ---------------------------------------------- // ------------------------------------ Parse start ---------------------------------------------- /** * 构建DateTime对象 * * @param dateStr Date字符串 * @param simpleDateFormat 格式化器 * @return DateTime对象 */ public static DateTime parse(String dateStr, SimpleDateFormat simpleDateFormat) { try { return new DateTime(simpleDateFormat.parse(dateStr)); } catch (Exception e) { throw new ToolBoxException(StrKit.format("Parse [{}] with format [{}] error!", dateStr, simpleDateFormat.toPattern()), e); } } /** * 将特定格式的日期转换为Date对象 * * @param dateString 特定格式的日期 * @param format 格式,例如yyyy-MM-dd * @return 日期对象 */ public static DateTime parse(String dateString, String format) { return parse(dateString, new SimpleDateFormat(format)); } /** * 格式yyyy-MM-dd HH:mm:ss * * @param dateString 标准形式的时间字符串 * @return 日期对象 */ public static DateTime parseDateTime(String dateString) { return parse(dateString, NORM_DATETIME_FORMAT.get()); } /** * 格式yyyy-MM-dd * * @param dateString 标准形式的日期字符串 * @return 日期对象 */ public static DateTime parseDate(String dateString) { return parse(dateString, NORM_DATE_FORMAT.get()); } /** * 格式HH:mm:ss * * @param timeString 标准形式的日期字符串 * @return 日期对象 */ public static DateTime parseTime(String timeString) { return parse(timeString, NORM_TIME_FORMAT.get()); } /** * 格式:
    * 1、yyyy-MM-dd HH:mm:ss
    * 2、yyyy-MM-dd
    * 3、HH:mm:ss
    * 4、yyyy-MM-dd HH:mm 5、yyyy-MM-dd HH:mm:ss.SSS * * @param dateStr 日期字符串 * @return 日期 */ public static DateTime parse(String dateStr) { if (null == dateStr) { return null; } dateStr = dateStr.trim(); int length = dateStr.length(); try { if (length == NORM_DATETIME_PATTERN.length()) { return parseDateTime(dateStr); } else if (length == NORM_DATE_PATTERN.length()) { return parseDate(dateStr); } else if (length == NORM_TIME_PATTERN.length()) { return parseTime(dateStr); } else if (length == NORM_DATETIME_MINUTE_PATTERN.length()) { return parse(dateStr, NORM_DATETIME_MINUTE_PATTERN); } else if (length >= NORM_DATETIME_MS_PATTERN.length() - 2) { return parse(dateStr, NORM_DATETIME_MS_PATTERN); } } catch (Exception e) { throw new ToolBoxException(StrKit.format("Parse [{}] with format normal error!", dateStr)); } // 没有更多匹配的时间格式 throw new ToolBoxException(StrKit.format(" [{}] format is not fit for date pattern!", dateStr)); } // ------------------------------------ Parse end ---------------------------------------------- // ------------------------------------ Offset start ---------------------------------------------- /** * 获取某天的开始时间 * * @param date 日期 * @return 某天的开始时间 */ public static DateTime getBeginTimeOfDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); return new DateTime(calendar.getTime()); } /** * 获取某天的结束时间 * * @param date 日期 * @return 某天的结束时间 */ public static DateTime getEndTimeOfDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); calendar.set(Calendar.MILLISECOND, 999); return new DateTime(calendar.getTime()); } /** * 昨天 * * @return 昨天 */ public static DateTime yesterday() { return offsiteDay(new DateTime(), -1); } /** * 上周 * * @return 上周 */ public static DateTime lastWeek() { return offsiteWeek(new DateTime(), -1); } /** * 上个月 * * @return 上个月 */ public static DateTime lastMouth() { return offsiteMonth(new DateTime(), -1); } /** * 偏移天 * * @param date 日期 * @param offsite 偏移天数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteDay(Date date, int offsite) { return offsiteDate(date, Calendar.DAY_OF_YEAR, offsite); } /** * 偏移周 * * @param date 日期 * @param offsite 偏移周数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteWeek(Date date, int offsite) { return offsiteDate(date, Calendar.WEEK_OF_YEAR, offsite); } /** * 偏移月 * * @param date 日期 * @param offsite 偏移月数,正数向未来偏移,负数向历史偏移 * @return 偏移后的日期 */ public static DateTime offsiteMonth(Date date, int offsite) { return offsiteDate(date, Calendar.MONTH, offsite); } /** * 获取指定日期偏移指定时间后的时间 * * @param date 基准日期 * @param calendarField 偏移的粒度大小(小时、天、月等)使用Calendar中的常数 * @param offsite 偏移量,正数为向后偏移,负数为向前偏移 * @return 偏移后的日期 */ public static DateTime offsiteDate(Date date, int calendarField, int offsite) { Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(calendarField, offsite); return new DateTime(cal.getTime()); } // ------------------------------------ Offset end ---------------------------------------------- /** * 判断两个日期相差的时长
    * 返回 minuend - subtrahend 的差 * * @param subtrahend 减数日期 * @param minuend 被减数日期 * @param diffField 相差的选项:相差的天、小时 * @return 日期差 */ public static long diff(Date subtrahend, Date minuend, long diffField) { long diff = minuend.getTime() - subtrahend.getTime(); return diff / diffField; } /** * 计时,常用于记录某段代码的执行时间,单位:纳秒 * * @param preTime 之前记录的时间 * @return 时间差,纳秒 */ public static long spendNt(long preTime) { return System.nanoTime() - preTime; } /** * 计时,常用于记录某段代码的执行时间,单位:毫秒 * * @param preTime 之前记录的时间 * @return 时间差,毫秒 */ public static long spendMs(long preTime) { return System.currentTimeMillis() - preTime; } /** * 格式化成yyMMddHHmm后转换为int型 * * @param date 日期 * @return int */ public static int toIntSecond(Date date) { return Integer.parseInt(format(date, "yyMMddHHmm")); } /** * 计算指定指定时间区间内的周数 * * @param start 开始时间 * @param end 结束时间 * @return 周数 */ public static int weekCount(Date start, Date end) { final Calendar startCalendar = Calendar.getInstance(); startCalendar.setTime(start); final Calendar endCalendar = Calendar.getInstance(); endCalendar.setTime(end); final int startWeekofYear = startCalendar.get(Calendar.WEEK_OF_YEAR); final int endWeekofYear = endCalendar.get(Calendar.WEEK_OF_YEAR); int count = endWeekofYear - startWeekofYear + 1; if (Calendar.SUNDAY != startCalendar.get(Calendar.DAY_OF_WEEK)) { count--; } return count; } /** * 计时器
    * 计算某个过程话费的时间,精确到毫秒 * * @return Timer */ public static Timer timer() { return new Timer(); } /** * 生日转为年龄,计算法定年龄 * @param birthDay 生日,标准日期字符串 * @return 年龄 * @throws Exception */ public static int ageOfNow(String birthDay) { return ageOfNow(parse(birthDay)); } /** * 生日转为年龄,计算法定年龄 * @param birthDay 生日 * @return 年龄 * @throws Exception */ public static int ageOfNow(Date birthDay) { return age(birthDay,date()); } /** * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄 * @param birthDay 生日 * @param dateToCompare 需要对比的日期 * @return 年龄 * @throws Exception */ public static int age(Date birthDay, Date dateToCompare) { Calendar cal = Calendar.getInstance(); cal.setTime(dateToCompare); if (cal.before(birthDay)) { throw new IllegalArgumentException(StrKit.format("Birthday is after date {}!", formatDate(dateToCompare))); } int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH); int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); cal.setTime(birthDay); int age = year - cal.get(Calendar.YEAR); int monthBirth = cal.get(Calendar.MONTH); if (month == monthBirth) { int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH); if (dayOfMonth < dayOfMonthBirth) { //如果生日在当月,但是未达到生日当天的日期,年龄减一 age--; } } else if (month < monthBirth){ //如果当前月份未达到生日的月份,年龄计算减一 age--; } return age; } /** * 计时器
    * 计算某个过程话费的时间,精确到毫秒 * * @author Looly * */ public static class Timer { private long time; private boolean isNano; public Timer() { this(false); } public Timer(boolean isNano) { this.isNano = isNano; start(); } /** * @return 开始计时并返回当前时间 */ public long start() { time = current(isNano); return time; } /** * @return 重新计时并返回从开始到当前的持续时间 */ public long durationRestart() { long now = current(isNano); long d = now - time; time = now; return d; } /** * @return 从开始到当前的持续时间 */ public long duration() { return current(isNano) - time; } } // ------------------------------------------------------------------------ Private method start /** * 获得指定日期年份和季节
    * 格式:[20131]表示2013年第一季度 * * @param cal 日期 */ private static String yearAndSeason(Calendar cal) { return new StringBuilder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH) / 3 + 1).toString(); } // ------------------------------------------------------------------------ Private method end } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/HexKit.java ================================================ package cn.enilu.material.admin.core.support; import cn.enilu.material.utils.StrKit; import java.nio.charset.Charset; /** * 十六进制(简写为hex或下标16)在数学中是一种逢16进1的进位制,一般用数字0到9和字母A到F表示(其中:A~F即10~15)。
    * 例如十进制数57,在二进制写作111001,在16进制写作39。
    * 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20
    * * 参考:https://my.oschina.net/xinxingegeya/blog/287476 * * @author Looly * */ public class HexKit { /** * 用于建立十六进制字符的输出的小写字符数组 */ private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * 用于建立十六进制字符的输出的大写字符数组 */ private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; //---------------------------------------------------------------------------------------------------- encode /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @return 十六进制char[] */ public static char[] encodeHex(byte[] data) { return encodeHex(data, true); } /** * 将字节数组转换为十六进制字符数组 * * @param str 字符串 * @param charset 编码 * @return 十六进制char[] */ public static char[] encodeHex(String str, Charset charset) { return encodeHex(StrKit.getBytes(str, charset), true); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 * @return 十六进制char[] */ public static char[] encodeHex(byte[] data, boolean toLowerCase) { return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @return 十六进制String */ public static String encodeHexStr(byte[] data) { return encodeHexStr(data, true); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 * @return 十六进制String */ public static String encodeHexStr(byte[] data, boolean toLowerCase) { return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } //---------------------------------------------------------------------------------------------------- decode /** * 将十六进制字符数组转换为字符串 * * @param hexStr 十六进制String * @param charset 编码 * @return 字符串 */ public static String decodeHexStr(String hexStr, Charset charset) { if(StrKit.isEmpty(hexStr)){ return hexStr; } return decodeHexStr(hexStr.toCharArray(), charset); } /** * 将十六进制字符数组转换为字符串 * * @param hexData 十六进制char[] * @param charset 编码 * @return 字符串 */ public static String decodeHexStr(char[] hexData, Charset charset) { return StrKit.str(decodeHex(hexData), charset); } /** * 将十六进制字符数组转换为字节数组 * * @param hexData 十六进制char[] * @return byte[] * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 */ public static byte[] decodeHex(char[] hexData) { int len = hexData.length; if ((len & 0x01) != 0) { throw new RuntimeException("Odd number of characters."); } byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(hexData[j], j) << 4; j++; f = f | toDigit(hexData[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } //---------------------------------------------------------------------------------------- Private method start /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制String */ private static String encodeHexStr(byte[] data, char[] toDigits) { return new String(encodeHex(data, toDigits)); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制char[] */ private static char[] encodeHex(byte[] data, char[] toDigits) { int l = data.length; char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return out; } /** * 将十六进制字符转换成一个整数 * * @param ch 十六进制char * @param index 十六进制字符在字符数组中的位置 * @return 一个整数 * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 */ private static int toDigit(char ch, int index) { int digit = Character.digit(ch, 16); if (digit == -1) { throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } //---------------------------------------------------------------------------------------- Private method end /** * 2进制转16进制 * @param bString 2进制字符串 * @return */ public static String binary2Hex(String bString) { if (bString == null || bString.equals("") || bString.length() % 8 != 0) return null; StringBuffer tmp = new StringBuffer(); int iTmp = 0; for (int i = 0; i < bString.length(); i += 4) { iTmp = 0; for (int j = 0; j < 4; j++) { iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1); } tmp.append(Integer.toHexString(iTmp)); } return tmp.toString(); } /** * 16进制转2进制 * @param hexString * @return */ public static String hex2Binary(String hexString) { if (hexString == null || hexString.length() % 2 != 0) return null; String bString = "", tmp; for (int i = 0; i < hexString.length(); i++) { tmp = "0000" + Integer.toBinaryString(Integer.parseInt(hexString.substring(i, i + 1), 16)); bString += tmp.substring(tmp.length() - 4); } return bString; } /** * 将二进制转换成16进制 * @param buf * @return */ public static String binary2Hex(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /** * 将16进制转换为二进制 * @param hexStr * @return */ public static byte[] hex2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++) { int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); result[i] = (byte) (high * 16 + low); } return result; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/ObjectKit.java ================================================ package cn.enilu.material.admin.core.support; /** * 一些通用的函数 * * @author Looly * */ public class ObjectKit { /** * 比较两个对象是否相等。
    * 相同的条件有两个,满足其一即可:
    * 1. obj1 == null && obj2 == null; 2. obj1.equals(obj2) * * @param obj1 对象1 * @param obj2 对象2 * @return 是否相等 */ public static boolean equals(Object obj1, Object obj2) { return (obj1 != null) ? (obj1.equals(obj2)) : (obj2 == null); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/PageKit.java ================================================ package cn.enilu.material.admin.core.support; /** * 分页工具类 * * @author xiaoleilu * */ public class PageKit { /** * 将页数和每页条目数转换为开始位置和结束位置
    * 此方法用于不包括结束位置的分页方法
    * 例如:
    * 页码:1,每页10 -> [0, 10]
    * 页码:2,每页10 -> [10, 20]
    * 。。。
    * * @param pageNo * 页码(从1计数) * @param countPerPage * 每页条目数 * @return 第一个数为开始位置,第二个数为结束位置 */ public static int[] transToStartEnd(int pageNo, int countPerPage) { if (pageNo < 1) { pageNo = 1; } if (countPerPage < 1) { countPerPage = 0; // LogKit.warn("Count per page [" + countPerPage + "] is not valid!"); } int start = (pageNo - 1) * countPerPage; int end = start + countPerPage; return new int[] { start, end }; } /** * 根据总数计算总页数 * * @param totalCount * 总数 * @param numPerPage * 每页数 * @return 总页数 */ public static int totalPage(int totalCount, int numPerPage) { if (numPerPage == 0) { return 0; } return totalCount % numPerPage == 0 ? (totalCount / numPerPage) : (totalCount / numPerPage + 1); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/support/exception/ToolBoxException.java ================================================ /** * Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.enilu.material.admin.core.support.exception; import cn.enilu.material.utils.StrKit; /** * 工具类初始化异常 */ public class ToolBoxException extends RuntimeException{ private static final long serialVersionUID = 8247610319171014183L; public ToolBoxException(Throwable e) { super(e.getMessage(), e); } public ToolBoxException(String message) { super(message); } public ToolBoxException(String messageTemplate, Object... params) { super(StrKit.format(messageTemplate, params)); } public ToolBoxException(String message, Throwable throwable) { super(message, throwable); } public ToolBoxException(Throwable throwable, String messageTemplate, Object... params) { super(StrKit.format(messageTemplate, params), throwable); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/ApiMenuFilter.java ================================================ package cn.enilu.material.admin.core.util; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.vo.SpringContextHolder; import cn.enilu.material.bean.vo.node.MenuNode; import java.util.ArrayList; import java.util.List; /** * api接口文档显示过滤 * * @author fengshuonan * @date 2017-08-17 16:55 */ public class ApiMenuFilter extends MenuNode { public static List build(List nodes) { //如果关闭了接口文档,则不显示接口文档菜单 AppProperties appProperties = SpringContextHolder.getBean(AppProperties.class); if (!appProperties.getSwaggerOpen()) { List menuNodesCopy = new ArrayList<>(); for (MenuNode menuNode : nodes) { if (Const.API_MENU_NAME.equals(menuNode.getName())) { continue; } else { menuNodesCopy.add(menuNode); } } nodes = menuNodesCopy; } return nodes; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/FileUtil.java ================================================ package cn.enilu.material.admin.core.util; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.exception.ExceptionEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileUtil { private static Logger log = LoggerFactory.getLogger(FileUtil.class); /** * NIO way */ public static byte[] toByteArray(String filename) { File f = new File(filename); if (!f.exists()) { log.error("文件未找到!" + filename); throw new ApplicationException(ExceptionEnum.FILE_NOT_FOUND); } FileChannel channel = null; FileInputStream fs = null; try { fs = new FileInputStream(f); channel = fs.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size()); while ((channel.read(byteBuffer)) > 0) { // do nothing // System.out.println("reading"); } return byteBuffer.array(); } catch (IOException e) { throw new ApplicationException(ExceptionEnum.FILE_READING_ERROR); } finally { try { channel.close(); } catch (IOException e) { throw new ApplicationException(ExceptionEnum.FILE_READING_ERROR); } try { fs.close(); } catch (IOException e) { throw new ApplicationException(ExceptionEnum.FILE_READING_ERROR); } } } /** * 删除目录 * * @author fengshuonan * @Date 2017/10/30 下午4:15 */ public static boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } } return dir.delete(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/HttpSessionHolder.java ================================================ package cn.enilu.material.admin.core.util; import javax.servlet.http.HttpSession; /** * 非Controller中获取当前session的工具类 * * @author fengshuonan * @date 2016年11月28日 上午10:24:31 */ public class HttpSessionHolder { private static ThreadLocal tl = new ThreadLocal(); public static void put(HttpSession s) { tl.set(s); } public static HttpSession get() { return tl.get(); } public static void remove() { tl.remove(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/KaptchaUtil.java ================================================ package cn.enilu.material.admin.core.util; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.bean.vo.SpringContextHolder; /** * 验证码工具类 */ public class KaptchaUtil { /** * 获取验证码开关 * * @author enilu.cn * @Date 2017/5/23 22:34 */ public static Boolean getKaptchaOnOff() { return SpringContextHolder.getBean(AppProperties.class).getKaptchaOpen(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/NumUtil.java ================================================ package cn.enilu.material.admin.core.util; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; /** * 数字格式化的类 * * @author fengshuonan * @date 2016年11月30日 下午5:58:40 */ public class NumUtil { /** * @Description 保留指定位数的小数(少的位数不补零) * @author fengshuonan */ public static String keepRandomPoint(Double value, int n) { if (value == null) { value = 0.00; return new BigDecimal(value).setScale(n, RoundingMode.HALF_UP).toString(); } else { return new BigDecimal(value).setScale(n, RoundingMode.HALF_UP).toString(); } } /** * @Description 浮点保留两位小数(少的位数不补零) * @author fengshuonan */ public static String keep2Point(double value) { return keepRandomPoint(value, 2); } /** * @Description 浮点保留1位小数(少的位数不补零) * @author fengshuonan */ public static String keep1Point(double value) { return keepRandomPoint(value, 1); } /** * @Description 浮点保留任意位小数(少位补零) * @author fengshuonan */ public static String keepRandomPointZero(double value, int n) { DecimalFormat df = new DecimalFormat("#0.00"); return df.format(Double.valueOf(keepRandomPoint(value, n))); } /** * @Description 浮点保留两位小数(少位补零) * @author fengshuonan */ public static String keep2PointZero(double value) { return keepRandomPointZero(value, 2); } /** * @Description 获取任意小数点位的百分比表示 * @author fengshuonan */ public static String percentRandomPoint(double value, int n) { NumberFormat percent = NumberFormat.getPercentInstance(); percent.setGroupingUsed(false); percent.setMaximumFractionDigits(n); return percent.format(value); } /** * @Description 百分比保留两位小数 * @author fengshuonan */ public static String percent2Point(double value) { return percentRandomPoint(value, 2); } /** * @Description 获取格式化经纬度后的小数(保留3位) * @author fengshuonan */ public static String latLngPoint(double value) { return keepRandomPoint(value, 3); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/PingYinUtil.java ================================================ package cn.enilu.material.admin.core.util; import java.util.Random; /*** * * 得到中文首字母 * * @author lxm_09 * */ public class PingYinUtil { public static void main(String[] args) { String str = "这是一个测试"; System.out.println("中文首字母:" + getPYIndexStr(str, true)); } /** * 返回首字母 */ public static String getPYIndexStr(String strChinese, boolean bUpCase) { try { StringBuffer buffer = new StringBuffer(); byte b[] = strChinese.getBytes("GBK");// 把中文转化成byte数组 for (int i = 0; i < b.length; i++) { if ((b[i] & 255) > 128) { int char1 = b[i++] & 255; char1 <<= 8;// 左移运算符用“<<”表示,是将运算符左边的对象,向左移动运算符右边指定的位数,并且在低位补零。其实,向左移n位,就相当于乘上2的n次方 int chart = char1 + (b[i] & 255); buffer.append(getPYIndexChar((char) chart, bUpCase)); continue; } char c = (char) b[i]; if (!Character.isJavaIdentifierPart(c))// 确定指定字符是否可以是 Java // 标识符中首字符以外的部分。 c = 'A'; buffer.append(c); } return buffer.toString(); } catch (Exception e) { System.out.println((new StringBuilder()).append("\u53D6\u4E2D\u6587\u62FC\u97F3\u6709\u9519") .append(e.getMessage()).toString()); } return null; } /** * 得到首字母 */ private static char getPYIndexChar(char strChinese, boolean bUpCase) { int charGBK = strChinese; char result; if (charGBK >= 45217 && charGBK <= 45252) result = 'A'; else if (charGBK >= 45253 && charGBK <= 45760) result = 'B'; else if (charGBK >= 45761 && charGBK <= 46317) result = 'C'; else if (charGBK >= 46318 && charGBK <= 46825) result = 'D'; else if (charGBK >= 46826 && charGBK <= 47009) result = 'E'; else if (charGBK >= 47010 && charGBK <= 47296) result = 'F'; else if (charGBK >= 47297 && charGBK <= 47613) result = 'G'; else if (charGBK >= 47614 && charGBK <= 48118) result = 'H'; else if (charGBK >= 48119 && charGBK <= 49061) result = 'J'; else if (charGBK >= 49062 && charGBK <= 49323) result = 'K'; else if (charGBK >= 49324 && charGBK <= 49895) result = 'L'; else if (charGBK >= 49896 && charGBK <= 50370) result = 'M'; else if (charGBK >= 50371 && charGBK <= 50613) result = 'N'; else if (charGBK >= 50614 && charGBK <= 50621) result = 'O'; else if (charGBK >= 50622 && charGBK <= 50905) result = 'P'; else if (charGBK >= 50906 && charGBK <= 51386) result = 'Q'; else if (charGBK >= 51387 && charGBK <= 51445) result = 'R'; else if (charGBK >= 51446 && charGBK <= 52217) result = 'S'; else if (charGBK >= 52218 && charGBK <= 52697) result = 'T'; else if (charGBK >= 52698 && charGBK <= 52979) result = 'W'; else if (charGBK >= 52980 && charGBK <= 53688) result = 'X'; else if (charGBK >= 53689 && charGBK <= 54480) result = 'Y'; else if (charGBK >= 54481 && charGBK <= 55289) result = 'Z'; else result = (char) (65 + (new Random()).nextInt(25)); if (!bUpCase) result = Character.toLowerCase(result); return result; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/RenderUtil.java ================================================ package cn.enilu.material.admin.core.util; import cn.enilu.material.bean.exception.ApplicationException; import com.alibaba.fastjson.JSON; import cn.enilu.material.bean.exception.ExceptionEnum; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * 渲染工具类 * * @author enilu.cn * @date 2017-08-25 14:13 */ public class RenderUtil { /** * 渲染json对象 */ public static void renderJson(HttpServletResponse response, Object jsonObject) { try { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); writer.write(JSON.toJSONString(jsonObject)); } catch (IOException e) { throw new ApplicationException(ExceptionEnum.WRITE_ERROR); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/ResKit.java ================================================ package cn.enilu.material.admin.core.util; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import java.io.IOException; /** * 资源文件相关的操作类 * * @author fengshuonan * @date 2016年11月17日 下午10:09:23 */ public class ResKit { /** * @Description 批量获取ClassPath下的资源文件 * @author fengshuonan */ public static Resource[] getClassPathResources(String pattern) { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { return resolver.getResources(pattern); } catch (IOException e) { throw new RuntimeException("加载resource文件时,找不到文件,所找文件为:" + pattern); } } /** * @Description 批量获取ClassPath下的资源文件 * @author fengshuonan */ public static String getClassPathFile(String file) { //return ResKit.class.getClassLoader().getResource(file).getPath(); return Thread.currentThread().getContextClassLoader().getResource(file).getPath(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/util/SqlUtil.java ================================================ package cn.enilu.material.admin.core.util; import java.util.ArrayList; import java.util.List; /** * sql语句工具类 * * @author fengshuonan * @date 2016年12月6日 下午1:01:54 */ public class SqlUtil { /** * @Description 根据集合的大小,输出相应个数"?" * @author fengshuonan */ public static String parse(List list) { String str = ""; if (list != null && list.size() > 0) { str = str + "?"; for (int i = 1; i < list.size(); i++) { str = str + ",?"; } } return str; } public static void main(String[] args) { ArrayList arrayList = new ArrayList<>(); arrayList.add(2); arrayList.add(2); System.out.println(parse(arrayList)); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/xss/XssFilter.java ================================================ package cn.enilu.material.admin.core.xss; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.List; public class XssFilter implements Filter { FilterConfig filterConfig = null; private List urlExclusion = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String servletPath = httpServletRequest.getServletPath(); if (urlExclusion != null && urlExclusion.contains(servletPath)) { chain.doFilter(request, response); } else { chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response); } } public List getUrlExclusion() { return urlExclusion; } public void setUrlExclusion(List urlExclusion) { this.urlExclusion = urlExclusion; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/core/xss/XssHttpServletRequestWrapper.java ================================================ package cn.enilu.material.admin.core.xss; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); } public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values[i]); } return encodedValues; } public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value == null) { return null; } return cleanXSS(value); } public String getHeader(String name) { String value = super.getHeader(name); if (value == null) return null; return cleanXSS(value); } private String cleanXSS(String value) { //You'll need to remove the spaces from the html entities below value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); value = value.replaceAll("'", "& #39;"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", ""); return value; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/lab/controller/LabController.java ================================================ package cn.enilu.material.admin.modular.lab.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * @author :enilu * @date :Created in 2019/9/3 16:53 */ @RequestMapping("/lab") @Controller public class LabController extends BaseController { @RequestMapping(value="/actuator",method = RequestMethod.GET) public String index(){ return "/lab/actuator.html"; } @RequestMapping(value="/gis",method = RequestMethod.GET) public String gis(){ return "/lab/gis.html"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/lab/package-info.java ================================================ /** * @author :enilu * @date :Created in 2019/9/3 16:54 */ package cn.enilu.material.admin.modular.lab; ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/message/MessageController.java ================================================ package cn.enilu.material.admin.modular.message; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.entity.message.Message; import cn.enilu.material.bean.vo.front.Rets; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.service.message.MessageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/message/history") public class MessageController extends BaseController { private static String PREFIX = "/message/history/"; @Autowired private MessageService messageService; /** * 跳转到参数首页 */ @RequestMapping(value = "",method = RequestMethod.GET) public String index() { return PREFIX + "message.html"; } /** * 跳转到消息详情 */ @RequestMapping(value = "/view/{id}",method = RequestMethod.GET) public String view(@PathVariable Long id, Model model) { Message message = messageService.get(id); model.addAttribute("item",message); return PREFIX + "message_view.html"; } @RequestMapping(value = "/list", method = RequestMethod.POST) @ResponseBody public Object list() { Page page = new PageFactory().defaultPage(); page = messageService.queryPage(page); page.setRecords(page.getRecords()); return super.packForBT(page); } @RequestMapping(value="/clear",method = RequestMethod.POST) @BussinessLog(value = "清空所有历史消息") @ResponseBody public Object clear() { messageService.clear(); return Rets.success(); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/message/MessagesenderController.java ================================================ package cn.enilu.material.admin.modular.message; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.dictmap.CommonDict; import cn.enilu.material.bean.entity.message.MessageSender; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.service.message.MessagesenderService; import cn.enilu.material.utils.ToolUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @Controller @RequestMapping("/message/sender") public class MessagesenderController extends BaseController { private static String PREFIX = "/message/sender/"; @Autowired private MessagesenderService messagesenderService; /** * 跳转到首页 */ @RequestMapping(value = "", method = RequestMethod.GET) public String index() { return PREFIX + "sender.html"; } /** * 跳转到添加参数 */ @RequestMapping("/add") public String add() { return PREFIX + "sender_add.html"; } /** * 跳转到修改参数 */ @RequestMapping("/update/{id}") public String update(@PathVariable Long id, Model model) { MessageSender sender = messagesenderService.get(id); model.addAttribute("item", sender); return PREFIX + "sender_edit.html"; } @RequestMapping(value = "/list", method = RequestMethod.POST) @ResponseBody public Object list() { Page page = new PageFactory().defaultPage(); page = messagesenderService.queryPage(page); page.setRecords(page.getRecords()); return packForBT(page); } @RequestMapping(method = RequestMethod.POST) @BussinessLog(value = "保存消息发送器", key = "name", dict = CommonDict.class) @ResponseBody public Object save(@ModelAttribute @Valid MessageSender tMessageSender) { messagesenderService.save(tMessageSender); return SUCCESS_TIP; } @RequestMapping(method = RequestMethod.DELETE) @BussinessLog(value = "删除消息发送器", key = "id", dict = CommonDict.class) @ResponseBody public Object remove(Long id) throws ApplicationException { if (ToolUtil.isEmpty(id)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } messagesenderService.delete(id); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/message/MessagetemplateController.java ================================================ package cn.enilu.material.admin.modular.message; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.dictmap.CommonDict; import cn.enilu.material.bean.entity.message.MessageSender; import cn.enilu.material.bean.entity.message.MessageTemplate; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.service.message.MessagesenderService; import cn.enilu.material.service.message.MessagetemplateService; import cn.enilu.material.utils.ToolUtil; import org.nutz.json.Json; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.List; @Controller @RequestMapping("/message/template") public class MessagetemplateController extends BaseController { private static String PREFIX = "/message/template/"; @Autowired private MessagetemplateService messagetemplateService; @Autowired private MessagesenderService messagesenderService; /** * 跳转到首页 */ @RequestMapping(value = "",method = RequestMethod.GET) public String index() { return PREFIX + "template.html"; } /** * 跳转到添加模板 */ @RequestMapping("/add") public String add(Model model) { List messageSenderList = messagesenderService.queryAll(); model.addAttribute("messageSenderList",messageSenderList); return PREFIX + "template_add.html"; } /** * 跳转到修改模板 */ @RequestMapping("/update/{id}") public String update(@PathVariable Long id, Model model) { MessageTemplate template = messagetemplateService.get(id); model.addAttribute("item",template); List messageSenderList = messagesenderService.queryAll(); model.addAttribute("messageSenderList",messageSenderList); return PREFIX + "template_edit.html"; } @RequestMapping(value = "/list", method = RequestMethod.POST) @ResponseBody public Object list() { Page page = new PageFactory().defaultPage(); page = messagetemplateService.queryPage(page); page.setRecords(page.getRecords()); System.out.println(Json.toJson(page)); return packForBT(page); } @RequestMapping(method = RequestMethod.POST) @BussinessLog(value = "编辑消息模板", key = "name", dict = CommonDict.class) @ResponseBody public Object save(@ModelAttribute @Valid MessageTemplate messageTemplate) { if(messageTemplate.getId()==null) { messagetemplateService.insert(messageTemplate); }else{ messagetemplateService.update(messageTemplate); } return SUCCESS_TIP; } @RequestMapping(method = RequestMethod.DELETE) @BussinessLog(value = "删除消息模板", key = "id", dict = CommonDict.class) @ResponseBody public Object remove(Long id) { if (ToolUtil.isEmpty(id)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } messagetemplateService.delete(id); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/BlackboardController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.dao.system.SysNoticeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; /** * 总览信息 * * @author fengshuonan * @Date 2017年3月4日23:05:54 */ @Controller @RequestMapping("/blackboard") public class BlackboardController extends BaseController { @Autowired SysNoticeRepository sysNoticeRepository; /** * 跳转到黑板 */ @RequestMapping("") public String blackboard(Model model) { List notices = (List) sysNoticeRepository.findAll(); model.addAttribute("noticeList",notices); return "/blackboard.html"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/CfgController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.dictmap.CfgDict; import cn.enilu.material.bean.entity.system.Cfg; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.service.system.CfgService; import cn.enilu.material.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; /** * CfgController * * @author enilu * @version 2018/8/9 0009 */ @Controller @RequestMapping("/cfg") public class CfgController extends BaseController { @Autowired private CfgService cfgService; private static String PREFIX = "/system/cfg/"; /** * 跳转到参数首页 */ @RequestMapping(value = "",method = RequestMethod.GET) public String index() { return PREFIX + "cfg.html"; } /** * 分页查询系统参数 */ @RequestMapping(value = "/list",method = RequestMethod.POST) @ResponseBody public Object list(@RequestParam(required = false) String cfgName, @RequestParam(required = false) String cfgValue) { Page page = new PageFactory().defaultPage(); if(StringUtils.isNotEmpty(cfgName)){ page.addFilter(SearchFilter.build("cfgName", SearchFilter.Operator.LIKE, cfgName)); } if(StringUtils.isNotEmpty(cfgValue)){ page.addFilter(SearchFilter.build("cfgValue", SearchFilter.Operator.LIKE, cfgValue)); } page = cfgService.queryPage(page); return packForBT(page); } /** * 跳转到添加参数页面 */ @RequestMapping(value = "/cfg_add",method = RequestMethod.GET) public String add() { return PREFIX + "cfg_add.html"; } /** * 新增参数 */ @RequestMapping(value = "/add",method = RequestMethod.POST) @ResponseBody @BussinessLog(value = "添加参数", key = "cfgName",dict = CfgDict.class) public Object add(@Valid Cfg cfg) { cfgService.saveOrUpdate(cfg); return SUCCESS_TIP; } /** * 跳转到修改参数 */ @RequestMapping(value = "/cfg_update/{cfgId}",method = RequestMethod.GET) public String update(@PathVariable Long cfgId, Model model) { Cfg cfg = cfgService.get(cfgId); model.addAttribute("item",cfg); return PREFIX + "cfg_edit.html"; } /** * 修改参数 */ @RequestMapping(value = "/update",method = RequestMethod.POST) @ResponseBody @BussinessLog(value = "编辑参数", key = "cfgName",dict = CfgDict.class) @Permission(Const.ADMIN_NAME) public Object update(@Valid Cfg cfg) { cfgService.saveOrUpdate(cfg); return SUCCESS_TIP; } /** * 删除参数 */ @RequestMapping(value = "/delete",method = RequestMethod.DELETE) @ResponseBody @BussinessLog(value = "删除参数", key = "cfgId",dict = CfgDict.class) @Permission(Const.ADMIN_NAME) public Object delete(@RequestParam Long cfgId) { cfgService.delete(cfgId); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/DeptController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.dictmap.DeptDict; import cn.enilu.material.bean.entity.system.Dept; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.service.system.DeptService; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.warpper.DeptWarpper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; /** * 部门控制器 * * @author fengshuonan * @Date 2017年2月17日20:27:22 */ @Controller @RequestMapping("/dept") public class DeptController extends BaseController { private String PREFIX = "/system/dept/"; @Autowired DeptService deptService; /** * 跳转到部门管理首页 */ @RequestMapping("") public String index() { return PREFIX + "dept.html"; } /** * 跳转到添加部门 */ @RequestMapping("/dept_add") public String deptAdd() { return PREFIX + "dept_add.html"; } /** * 跳转到修改部门 */ @Permission @RequestMapping("/dept_update/{deptId}") public String deptUpdate(@PathVariable Long deptId, Model model) { Dept dept = deptService.get(deptId); model.addAttribute(dept); model.addAttribute("pName", ConstantFactory.me().getDeptName(dept.getPid())); LogObjectHolder.me().set(dept); return PREFIX + "dept_edit.html"; } /** * 获取部门的tree列表 */ @RequestMapping(value = "/tree") @ResponseBody public List tree() { List tree = this.deptService.tree(); tree.add(ZTreeNode.createParent()); return tree; } /** * 新增部门 */ @BussinessLog(value = "添加部门", key = "simplename", dict = DeptDict.class) @RequestMapping(value = "/add") @Permission @ResponseBody public Object add(Dept dept) { if (ToolUtil.isOneEmpty(dept, dept.getSimplename())) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //完善pids,根据pid拿到pid的pids deptService.deptSetPids(dept); return deptService.insert(dept); } /** * 获取所有部门列表 */ @RequestMapping(value = "/list") @Permission @ResponseBody public Object list(String condition) { List list = this.deptService.query(condition); return super.warpObject(new DeptWarpper(BeanUtil.objectsToMaps(list))); } /** * 部门详情 */ @RequestMapping(value = "/detail/{deptId}") @Permission @ResponseBody public Object detail(@PathVariable("deptId") Long deptId) { return deptService.get(deptId); } /** * 修改部门 */ @BussinessLog(value = "修改部门", key = "simplename", dict = DeptDict.class) @RequestMapping(value = "/update") @Permission(Const.ADMIN_NAME) @ResponseBody public Object update(Dept dept) { if (ToolUtil.isEmpty(dept) || dept.getId() == null) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } deptService.deptSetPids(dept); deptService.update(dept); return SUCCESS_TIP; } /** * 删除部门 */ @BussinessLog(value = "删除部门", key = "deptId", dict = DeptDict.class) @RequestMapping(value = "/delete") @Permission(Const.ADMIN_NAME) @ResponseBody public Object delete(@RequestParam Long deptId) { //缓存被删除的部门名称 LogObjectHolder.me().set(ConstantFactory.me().getDeptName(deptId)); deptService.deleteDept(deptId); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/DictController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.dictmap.DictMap; import cn.enilu.material.bean.entity.system.Dict; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.service.system.DictService; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.warpper.DictWarpper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; /** * 字典控制器 * * @author fengshuonan * @Date 2017年4月26日 12:55:31 */ @Controller @RequestMapping("/dict") public class DictController extends BaseController { private String PREFIX = "/system/dict/"; @Autowired DictService dictService; /** * 跳转到字典管理首页 */ @RequestMapping("") public String index() { return PREFIX + "dict.html"; } /** * 跳转到添加字典 */ @RequestMapping("/dict_add") public String deptAdd() { return PREFIX + "dict_add.html"; } /** * 跳转到修改字典 */ @Permission(Const.ADMIN_NAME) @RequestMapping("/dict_edit/{dictId}") public String deptUpdate(@PathVariable Long dictId, Model model) { Dict dict = dictService.get(dictId); model.addAttribute("dict", dict); List subDicts = dictService.queryByPid(dictId); model.addAttribute("subDicts", subDicts); LogObjectHolder.me().set(dict); return PREFIX + "dict_edit.html"; } /** * 新增字典 * * @param dictValues 格式例如 "1:启用;2:禁用;3:冻结" */ @BussinessLog(value = "添加字典记录", key = "dictName,dictValues", dict = DictMap.class) @RequestMapping(value = "/add") @Permission(Const.ADMIN_NAME) @ResponseBody public Object add(String dictName, String dictValues) { if (ToolUtil.isOneEmpty(dictName, dictValues)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } this.dictService.addDict(dictName, dictValues); return SUCCESS_TIP; } /** * 获取所有字典列表 */ @RequestMapping(value = "/list") @Permission(Const.ADMIN_NAME) @ResponseBody public Object list(String condition) { List list = dictService.queryByPid(0L); return super.warpObject(new DictWarpper(BeanUtil.objectsToMaps(list))); } /** * 字典详情 */ @RequestMapping(value = "/detail/{dictId}") @Permission(Const.ADMIN_NAME) @ResponseBody public Object detail(@PathVariable("dictId") Long dictId) { return dictService.get(dictId); } /** * 修改字典 */ @BussinessLog(value = "修改字典", key = "dictName,dictValues", dict = DictMap.class) @RequestMapping(value = "/update") @Permission(Const.ADMIN_NAME) @ResponseBody public Object update(Long dictId, String dictName, String dictValues) { if (ToolUtil.isOneEmpty(dictId, dictName, dictValues)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } dictService.editDict(dictId, dictName, dictValues); return SUCCESS_TIP; } /** * 删除字典记录 */ @BussinessLog(value = "删除字典记录", key = "dictId", dict = DictMap.class) @RequestMapping(value = "/delete") @Permission(Const.ADMIN_NAME) @ResponseBody public Object delete(@RequestParam Long dictId) { //缓存被删除的名称 LogObjectHolder.me().set(ConstantFactory.me().getDictName(dictId)); this.dictService.delteDict(dictId); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/KaptchaController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.admin.core.util.FileUtil; import com.google.code.kaptcha.Constants; import com.google.code.kaptcha.Producer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.awt.image.BufferedImage; import java.io.IOException; /** * 验证码生成 * * @author fengshuonan * @date 2017-05-05 23:10 */ @Controller @RequestMapping("/kaptcha") public class KaptchaController { @Resource private AppProperties appProperties; @Autowired Producer producer; /** * 生成验证码 */ @RequestMapping("") public void index(HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); response.setDateHeader("Expires", 0); // Set standard HTTP/1.1 no-cache headers. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); // Set IE extended HTTP/1.1 no-cache headers (use addHeader). response.addHeader("Cache-Control", "post-check=0, pre-check=0"); // Set standard HTTP/1.0 no-cache header. response.setHeader("Pragma", "no-cache"); // return a jpeg response.setContentType("image/jpeg"); // create the text for the image String capText = producer.createText(); // store the text in the session session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText); // create the image with the text BufferedImage bi = producer.createImage(capText); ServletOutputStream out = null; try { out = response.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } // write the data out try { ImageIO.write(bi, "jpg", out); } catch (IOException e) { e.printStackTrace(); } try { try { out.flush(); } catch (IOException e) { e.printStackTrace(); } } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 返回图片 * * @author enilu.cn * @Date 2017/5/24 23:00 */ @RequestMapping("/{pictureId}") public void renderPicture(@PathVariable("pictureId") String pictureId, HttpServletResponse response) { String path = appProperties.getFileUploadPath() + pictureId; try { byte[] bytes = FileUtil.toByteArray(path); response.getOutputStream().write(bytes); }catch (Exception e){ //如果找不到图片就返回一个默认图片 try { response.sendRedirect("/static/img/avatar.png"); } catch (IOException e1) { e1.printStackTrace(); } } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/LogController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.admin.core.support.BeanKit; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.constant.state.BizLogType; import cn.enilu.material.bean.entity.system.OperationLog; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.service.system.OperationLogService; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.DateUtil; import cn.enilu.material.warpper.LogWarpper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.Map; /** * 日志管理的控制器 * * @author fengshuonan * @Date 2017年4月5日 19:45:36 */ @Controller @RequestMapping("/log") public class LogController extends BaseController { private static String PREFIX = "/system/log/"; @Autowired private OperationLogService operationLogService; /** * 跳转到日志管理的首页 */ @RequestMapping("") public String index() { return PREFIX + "log.html"; } /** * 查询操作日志列表 */ @RequestMapping("/list") @Permission(Const.ADMIN_NAME) @ResponseBody public Object list(@RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String logName, @RequestParam(required = false) Integer logType) { Page page = new PageFactory().defaultPage(); page.addFilter("createTime", SearchFilter.Operator.GTE, DateUtil.parseDate(beginTime)); page.addFilter("createTime", SearchFilter.Operator.LTE, DateUtil.parseDate(endTime)); page.addFilter( "logname", SearchFilter.Operator.LIKE, logName); if(logType!=0) { page.addFilter(SearchFilter.build("logtype", SearchFilter.Operator.EQ, BizLogType.valueOf(logType))); } page = operationLogService.queryPage(page); page.setRecords((List) new LogWarpper(BeanUtil.objectsToMaps(page.getRecords())).warp()); return super.packForBT(page); } /** * 查询操作日志详情 */ @RequestMapping("/detail/{id}") @Permission(Const.ADMIN_NAME) @ResponseBody public Object detail(@PathVariable Long id) { OperationLog operationLog = operationLogService.get(id); Map stringObjectMap = BeanKit.beanToMap(operationLog); return super.warpObject(new LogWarpper(stringObjectMap)); } /** * 清空日志 */ @BussinessLog(value = "清空业务日志") @RequestMapping("/delLog") @Permission(Const.ADMIN_NAME) @ResponseBody public Object delLog() { operationLogService.clear(); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/LoginController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.admin.core.util.KaptchaUtil; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.exception.InvalidKaptchaException; import cn.enilu.material.platform.log.LogManager; import cn.enilu.material.platform.log.LogTaskFactory; import cn.enilu.material.service.system.MenuService; import cn.enilu.material.service.system.UserService; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.HttpKit; import cn.enilu.material.utils.ToolUtil; import com.google.code.kaptcha.Constants; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; /** * 登录控制器 * * @author fengshuonan * @Date 2017年1月10日 下午8:25:24 */ @Controller public class LoginController extends BaseController { Logger logger = LoggerFactory.getLogger(LoginController.class); @Autowired MenuService menuService; @Autowired UserService userService; /** * 跳转到主页 */ @RequestMapping(value = "/", method = RequestMethod.GET) public String index(Model model) { List roleList = ShiroKit.getUser().getRoleList(); if (roleList == null || roleList.size() == 0) { ShiroKit.getSubject().logout(); model.addAttribute("tips", "该用户没有角色,无法登陆"); return "/login.html"; } return "/index.html"; } /** * 跳转到登录页面 */ @RequestMapping(value = "/login", method = RequestMethod.GET) public String login() { if (ShiroKit.isAuthenticated() || ShiroKit.getUser() != null) { return REDIRECT + "/"; } else { return "/login.html"; } } /** * 点击登录执行的动作 */ @RequestMapping(value = "/login", method = RequestMethod.POST) public String loginVali() { String username = super.getPara("username").trim(); String password = super.getPara("password").trim(); String remember = super.getPara("remember"); //验证验证码是否正确 if (KaptchaUtil.getKaptchaOnOff()) { String kaptcha = super.getPara("kaptcha").trim(); String code = (String) super.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); if (ToolUtil.isEmpty(kaptcha) || !kaptcha.equalsIgnoreCase(code)) { throw new InvalidKaptchaException(); } } Subject currentUser = ShiroKit.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray()); if ("on".equals(remember)) { token.setRememberMe(true); } else { token.setRememberMe(false); } currentUser.login(token); ShiroUser shiroUser = ShiroKit.getUser(); super.getSession().setAttribute("shiroUser", shiroUser); super.getSession().setAttribute("username", shiroUser.getAccount()); LogManager.me().executeLog(LogTaskFactory.loginLog(shiroUser.getId(), HttpKit.getIp())); ShiroKit.getSession().setAttribute("sessionFlag", true); return REDIRECT + "/"; } /** * 退出登录 */ @RequestMapping(value = "/logout", method = RequestMethod.GET) public String logOut() { LogManager.me().executeLog(LogTaskFactory.exitLog(ShiroKit.getUser().getId(), HttpKit.getIp())); ShiroKit.getSubject().logout(); return REDIRECT + "/login"; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/LoginLogController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.entity.system.LoginLog; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.service.system.LoginLogService; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.DateUtil; import cn.enilu.material.warpper.LogWarpper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; /** * 日志管理的控制器 * * @author fengshuonan * @Date 2017年4月5日 19:45:36 */ @Controller @RequestMapping("/loginLog") public class LoginLogController extends BaseController { private static String PREFIX = "/system/log/"; @Autowired private LoginLogService loginlogService; /** * 跳转到日志管理的首页 */ @RequestMapping("") public String index() { return PREFIX + "login_log.html"; } /** * 查询登录日志列表 */ @RequestMapping("/list") @Permission(Const.ADMIN_NAME) @ResponseBody public Object list(@RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String logName) { Page page = new PageFactory().defaultPage(); page.addFilter("createTime", SearchFilter.Operator.GTE, DateUtil.parseDate(beginTime)); page.addFilter("createTime", SearchFilter.Operator.LTE, DateUtil.parseDate(endTime)); page.addFilter( "logname", SearchFilter.Operator.LIKE, logName); page = loginlogService.queryPage(page); page.setRecords((List) new LogWarpper(BeanUtil.objectsToMaps(page.getRecords())).warp()); return super.packForBT(page); } /** * 清空日志 */ @BussinessLog("清空登录日志") @RequestMapping("/delLoginLog") @Permission(Const.ADMIN_NAME) @ResponseBody public Object delLog() { loginlogService.clear(); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/MenuController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.admin.core.base.tips.Tip; import cn.enilu.material.admin.core.support.BeanKit; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.constant.state.MenuStatus; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.dictmap.MenuDict; import cn.enilu.material.bean.entity.system.Menu; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.exception.ExceptionEnum; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.MenuService; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.Lists; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.warpper.MenuWarpper; import com.google.common.base.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.validation.Valid; import java.util.List; import java.util.Map; /** * 菜单控制器 * * @author fengshuonan * @Date 2017年2月12日21:59:14 */ @Controller @RequestMapping("/menu") public class MenuController extends BaseController { private static String PREFIX = "/system/menu/"; @Autowired MenuService menuService; /** * 跳转到菜单列表列表页面 */ @RequestMapping("") public String index() { return PREFIX + "menu.html"; } /** * 跳转到菜单列表列表页面 */ @RequestMapping(value = "/menu_add") public String menuAdd() { return PREFIX + "menu_add.html"; } /** * 跳转到菜单详情列表页面 */ @Permission(Const.ADMIN_NAME) @RequestMapping(value = "/menu_edit/{menuId}") public String menuEdit(@PathVariable Long menuId, Model model) { if (ToolUtil.isEmpty(menuId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } Menu menu = menuService.get(menuId); //获取父级菜单的id Menu pMenu = menuService.findByCode(menu.getPcode()); //如果父级是顶级菜单 if (pMenu == null) { menu.setPcode("0"); } Map menuMap = BeanKit.beanToMap(menu); menuMap.put("pcodeName", ConstantFactory.me().getMenuNameByCode(menu.getPcode())); model.addAttribute("menu", menuMap); LogObjectHolder.me().set(menu); return PREFIX + "menu_edit.html"; } /** * 修该菜单 */ @Permission(Const.ADMIN_NAME) @RequestMapping(value = "/edit") @BussinessLog(value = "修改菜单", key = "name", dict = MenuDict.class) @ResponseBody public Tip edit(@Valid Menu menu, BindingResult result) { //设置父级菜单编号 menuService.menuSetPcode(menu); menu.setStatus(MenuStatus.ENABLE.getCode()); this.menuService.update(menu); return SUCCESS_TIP; } /** * 获取菜单列表 */ @Permission @RequestMapping(value = "/list") @ResponseBody public Object list(@RequestParam(required = false) String menuName, @RequestParam(required = false) String level) { List menus = null; if (Strings.isNullOrEmpty(menuName) && Strings.isNullOrEmpty(level)) { menus = menuService.queryAll(); } if (!Strings.isNullOrEmpty(menuName) && !Strings.isNullOrEmpty(level)) { menus = menuService.queryAll(Lists.newArrayList( SearchFilter.build("name", SearchFilter.Operator.LIKE,menuName), SearchFilter.build("levels",level) )); } if (!Strings.isNullOrEmpty(menuName) && Strings.isNullOrEmpty(level)) { menus = menuService.findByNameLike( menuName ); } if (Strings.isNullOrEmpty(menuName) && !Strings.isNullOrEmpty(level)) { menus = menuService.findByLevels(Integer.valueOf(level)); } return super.warpObject(new MenuWarpper(BeanUtil.objectsToMaps(menus))); } /** * 新增菜单 */ @Permission(Const.ADMIN_NAME) @RequestMapping(value = "/add") @BussinessLog(value = "菜单新增", key = "name", dict = MenuDict.class) @ResponseBody public Tip add(@Valid Menu menu, BindingResult result) { if (result.hasErrors()) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //判断是否存在该编号 String existedMenuName = ConstantFactory.me().getMenuNameByCode(menu.getCode()); if (ToolUtil.isNotEmpty(existedMenuName)) { throw new ApplicationException(BizExceptionEnum.EXISTED_THE_MENU); } //设置父级菜单编号 menuService.menuSetPcode(menu); menu.setStatus(MenuStatus.ENABLE.getCode()); this.menuService.insert(menu); return SUCCESS_TIP; } /** * 删除菜单 */ @Permission(Const.ADMIN_NAME) @RequestMapping(value = "/remove") @BussinessLog(value = "删除菜单", key = "menuId", dict = MenuDict.class) @ResponseBody public Tip remove(@RequestParam Long menuId) { if (ToolUtil.isEmpty(menuId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //缓存菜单的名称 LogObjectHolder.me().set(ConstantFactory.me().getMenuName(menuId)); this.menuService.delMenuContainSubMenus(menuId); return SUCCESS_TIP; } /** * 查看菜单 */ @RequestMapping(value = "/view/{menuId}") @ResponseBody public Tip view(@PathVariable Long menuId) { if (ToolUtil.isEmpty(menuId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } menuService.get(menuId); return SUCCESS_TIP; } /** * 获取菜单列表(首页用) */ @RequestMapping(value = "/menuTreeList") @ResponseBody public List menuTreeList() { List roleTreeList = this.menuService.menuTreeList(); return roleTreeList; } /** * 获取菜单列表(选择父级菜单用) */ @RequestMapping(value = "/selectMenuTreeList") @ResponseBody public List selectMenuTreeList() { List roleTreeList = this.menuService.menuTreeList(); roleTreeList.add(ZTreeNode.createParent()); return roleTreeList; } /** * 获取角色列表 */ @RequestMapping(value = "/menuTreeListByRoleId/{roleId}") @ResponseBody public List menuTreeListByRoleId(@PathVariable Integer roleId) { List menuIds = this.menuService.getMenuIdsByRoleId(roleId); if (ToolUtil.isEmpty(menuIds)) { List roleTreeList = this.menuService.menuTreeList(); return roleTreeList; } else { List roleTreeListByUserId = this.menuService.menuTreeListByMenuIds(menuIds); return roleTreeListByUserId; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/NoticeController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.dictmap.NoticeMap; import cn.enilu.material.bean.entity.system.Notice; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.NoticeService; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.warpper.NoticeWrapper; import com.google.common.base.Strings; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import java.util.List; /** * 通知控制器 * * @author fengshuonan * @Date 2017-05-09 23:02:21 */ @Controller @RequestMapping("/notice") public class NoticeController extends BaseController { private String PREFIX = "/system/notice/"; @Resource private NoticeService noticeService; /** * 跳转到通知列表首页 */ @RequestMapping("") public String index() { return PREFIX + "notice.html"; } /** * 跳转到添加通知 */ @RequestMapping("/notice_add") public String noticeAdd() { return PREFIX + "notice_add.html"; } /** * 跳转到修改通知 */ @RequestMapping("/notice_update/{noticeId}") public String noticeUpdate(@PathVariable Long noticeId, Model model) { Notice notice = ConstantFactory.me().getNotice(noticeId); model.addAttribute("notice",notice); LogObjectHolder.me().set(notice); return PREFIX + "notice_edit.html"; } /** * 跳转到首页通知 */ @RequestMapping("/hello") public String hello() { List notices = (List) noticeService.queryAll(); super.setAttr("noticeList",notices); return "/blackboard.html"; } /** * 获取通知列表 */ @RequestMapping(value = "/list") @ResponseBody public Object list(String condition) { List list = null; if(Strings.isNullOrEmpty(condition)) { list = (List) this.noticeService.queryAll(); }else{ list = noticeService.findByTitleLike("%"+condition+"%"); } return super.warpObject(new NoticeWrapper(BeanUtil.objectsToMaps(list))); } /** * 新增通知 */ @RequestMapping(value = "/add") @ResponseBody @BussinessLog(value = "新增通知",key = "title",dict = NoticeMap.class) public Object add(Notice notice) { if (ToolUtil.isOneEmpty(notice, notice.getTitle(), notice.getContent())) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } noticeService.insert(notice); return SUCCESS_TIP; } /** * 删除通知 */ @RequestMapping(value = "/delete") @ResponseBody @BussinessLog(value = "删除通知",key = "noticeId",dict = NoticeMap.class) public Object delete(@RequestParam Long noticeId) { //缓存通知名称 LogObjectHolder.me().set(ConstantFactory.me().getNoticeTitle(noticeId)); this.noticeService.delete(noticeId); return SUCCESS_TIP; } /** * 修改通知 */ @RequestMapping(value = "/update") @ResponseBody @BussinessLog(value = "修改通知",key = "title",dict = NoticeMap.class) public Object update(Notice notice) { if (ToolUtil.isOneEmpty(notice, notice.getId(), notice.getTitle(), notice.getContent())) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } Notice old = ConstantFactory.me().getNotice(notice.getId()); old.setTitle(notice.getTitle()); old.setContent(notice.getContent()); noticeService.update(old); return SUCCESS_TIP; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/RoleController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.tips.ErrorTip; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.dictmap.RoleDict; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.admin.core.base.tips.Tip; import cn.enilu.material.admin.core.cache.CacheKit; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.bean.vo.query.SearchFilter; import cn.enilu.material.service.system.UserService; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.warpper.RoleWarpper; import cn.enilu.material.bean.vo.node.ZTreeNode; import cn.enilu.material.bean.constant.cache.Cache; import cn.enilu.material.bean.entity.system.Role; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.RoleService; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.utils.Convert; import cn.enilu.material.utils.ToolUtil; import com.google.common.base.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.validation.Valid; import java.util.List; /** * 角色控制器 * * @author fengshuonan * @Date 2017年2月12日21:59:14 */ @Controller @RequestMapping("/role") public class RoleController extends BaseController { private static String PREFIX = "/system/role"; @Autowired private RoleService roleService; @Autowired private UserService userService; /** * 跳转到角色列表页面 */ @RequestMapping("") public String index() { return PREFIX + "/role.html"; } /** * 跳转到添加角色 */ @RequestMapping(value = "/role_add") public String roleAdd() { return PREFIX + "/role_add.html"; } /** * 跳转到修改角色 */ @Permission @RequestMapping(value = "/role_edit/{roleId}") public String roleEdit(@PathVariable Long roleId, Model model) { if (ToolUtil.isEmpty(roleId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } Role role = roleService.get(roleId); model.addAttribute(role); model.addAttribute("pName", ConstantFactory.me().getSingleRoleName(role.getPid())); model.addAttribute("deptName", ConstantFactory.me().getDeptName(role.getDeptid())); LogObjectHolder.me().set(role); return PREFIX + "/role_edit.html"; } /** * 跳转到角色分配 */ @Permission @RequestMapping(value = "/role_assign/{roleId}") public String roleAssign(@PathVariable("roleId") Long roleId, Model model) { if (ToolUtil.isEmpty(roleId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } model.addAttribute("roleId", roleId); model.addAttribute("roleName", ConstantFactory.me().getSingleRoleName(roleId)); return PREFIX + "/role_assign.html"; } /** * 获取角色列表 */ @Permission @RequestMapping(value = "/list") @ResponseBody public Object list(@RequestParam(required = false) String roleName) { List roles = null; if(Strings.isNullOrEmpty(roleName)) { roles = (List) roleService.queryAll(); }else{ roles = roleService.findByName(roleName); } return super.warpObject(new RoleWarpper(BeanUtil.objectsToMaps(roles))); } /** * 角色新增 */ @RequestMapping(value = "/add") @BussinessLog(value = "添加角色", key = "name", dict = RoleDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip add(@Valid Role role, BindingResult result) { if (result.hasErrors()) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } role.setId(null); roleService.insert(role); return SUCCESS_TIP; } /** * 角色修改 */ @RequestMapping(value = "/edit") @BussinessLog(value = "修改角色", key = "name", dict = RoleDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip edit(@Valid Role role, BindingResult result) { if (result.hasErrors()) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } this.roleService.update(role); //删除缓存 CacheKit.removeAll(Cache.CONSTANT); return SUCCESS_TIP; } /** * 删除角色 */ @RequestMapping(value = "/remove") @BussinessLog(value = "删除角色", key = "roleId", dict = RoleDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip remove(@RequestParam Long roleId) { if (ToolUtil.isEmpty(roleId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //不能删除超级管理员角色 if(roleId.intValue() ==Const.ADMIN_ROLE_ID){ throw new ApplicationException(BizExceptionEnum.CANT_DELETE_ADMIN); } List userList = userService.queryAll(SearchFilter.build("roleid", SearchFilter.Operator.EQ,String.valueOf(roleId))); if(!userList.isEmpty()){ return new ErrorTip(400,"有用户使用该角色,禁止删除"); } //缓存被删除的角色名称 LogObjectHolder.me().set(ConstantFactory.me().getSingleRoleName(roleId)); this.roleService.delRoleById(roleId); //删除缓存 CacheKit.removeAll(Cache.CONSTANT); return SUCCESS_TIP; } /** * 查看角色 */ @RequestMapping(value = "/view/{roleId}") @ResponseBody public Tip view(@PathVariable Long roleId) { if (ToolUtil.isEmpty(roleId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } roleService.get(roleId); return SUCCESS_TIP; } /** * 配置权限 */ @RequestMapping("/setAuthority") @BussinessLog(value = "配置权限", key = "roleId,ids", dict = RoleDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip setAuthority(@RequestParam("roleId") Long roleId, @RequestParam("ids") String ids) { if (ToolUtil.isOneEmpty(roleId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } roleService.setAuthority(roleId, ids); return SUCCESS_TIP; } /** * 获取角色列表 */ @RequestMapping(value = "/roleTreeList") @ResponseBody public List roleTreeList() { List roleTreeList = roleService.roleTreeList(); roleTreeList.add(ZTreeNode.createParent()); return roleTreeList; } /** * 获取角色列表 */ @RequestMapping(value = "/roleTreeListByUserId/{userId}") @ResponseBody public List roleTreeListByUserId(@PathVariable Long userId) { User theUser = userService.get(userId); String roleid = theUser.getRoleid(); if (ToolUtil.isEmpty(roleid)) { List roleTreeList = roleService.roleTreeList(); return roleTreeList; } else { Long[] roleIds = Convert.toLongArray(",", roleid); List roleTreeListByUserId = this.roleService.roleTreeListByRoleId(roleIds); return roleTreeListByUserId; } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/TaskController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.constant.factory.PageFactory; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.dictmap.TaskDict; import cn.enilu.material.bean.entity.system.Task; import cn.enilu.material.bean.entity.system.TaskLog; import cn.enilu.material.bean.vo.query.Page; import cn.enilu.material.service.task.TaskService; import cn.enilu.material.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.validation.Valid; /** * Created on 2018/4/9 0009. * 系统参数 * @author enilu */ @Controller @RequestMapping("/task") public class TaskController extends BaseController { private Logger logger = LoggerFactory.getLogger(TaskController.class); @Autowired private TaskService taskService; private String PREFIX = "/system/task/"; /** * 跳转到定时任务管理首页 */ @RequestMapping("") public String index() { return PREFIX + "task.html"; } /** * 跳转到添加定时任务管理 */ @RequestMapping("/task_add") public String add() { return PREFIX + "task_add.html"; } /** * 跳转到修改定时任务管理 */ @RequestMapping("/task_update/{taskId}") public String update(@PathVariable Long taskId, Model model) { Task task = taskService.get(taskId); model.addAttribute("item",task); return PREFIX + "task_edit.html"; } /** * 获取定时任务管理列表 */ @RequestMapping(value = "/list") @ResponseBody public Object list(String condition) { if(StringUtils.isNullOrEmpty(condition)) { return taskService.queryAll(); }else{ return taskService.findByNameLike("%"+condition+"%"); } } /** * 新增定时任务管理 */ @RequestMapping(value = "/add") @ResponseBody @BussinessLog(value = "添加定时任务", key = "name",dict = TaskDict.class) @Permission(Const.ADMIN_NAME) public Object add(@Valid Task task) { taskService.save(task); return SUCCESS_TIP; } /** * 删除定时任务管理 */ @RequestMapping(value = "/delete") @ResponseBody @BussinessLog(value = "删除定时任务", key = "taskId",dict = TaskDict.class) @Permission(Const.ADMIN_NAME) public Object delete(@RequestParam Long taskId) { taskService.delete(taskId); return SUCCESS_TIP; } @RequestMapping("/disable") @ResponseBody @BussinessLog(value = "禁用定时任务", key = "taskId",dict = TaskDict.class) @Permission(Const.ADMIN_NAME) public Object disable(@RequestParam Long taskId ) { taskService.disable(taskId); return SUCCESS_TIP; } @RequestMapping("/enable") @ResponseBody @BussinessLog(value = "启用定时任务", key = "taskId",dict = TaskDict.class) @Permission(Const.ADMIN_NAME) public Object enable(@RequestParam Long taskId ) { taskService.enable(taskId); return SUCCESS_TIP; } /** * 修改定时任务管理 */ @RequestMapping(value = "/update") @ResponseBody @BussinessLog(value = "编辑定时任务", key = "name",dict = TaskDict.class) public Object update(@Valid Task task) { Task old = taskService.get(task.getId()); old.setName(task.getName()); old.setCron(task.getCron()); old.setNote(task.getNote()); old.setJobClass(task.getJobClass()); old.setData(task.getData()); taskService.update(old); return SUCCESS_TIP; } /** * 定时任务管理详情 */ @RequestMapping(value = "/detail/{taskId}") @ResponseBody public Object detail(@PathVariable("taskId") Long taskId) { return taskService.get(taskId); } @RequestMapping(value = "/viewLog/{taskId}") public String viewLog(@PathVariable("taskId") Long taskId,Model model) { model.addAttribute("taskId",taskId); return PREFIX+"task_log.html"; } @RequestMapping(value="/logList/{taskId}") @ResponseBody public Object listList(@PathVariable("taskId") Long taskId) { Page page = new PageFactory().defaultPage(); page = taskService.getTaskLogs(page,taskId); return super.packForBT(page); } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/controller/UserMgrController.java ================================================ package cn.enilu.material.admin.modular.system.controller; import cn.enilu.material.admin.config.properties.AppProperties; import cn.enilu.material.admin.core.base.controller.BaseController; import cn.enilu.material.admin.core.base.tips.Tip; import cn.enilu.material.bean.constant.Const; import cn.enilu.material.bean.constant.state.ManagerStatus; import cn.enilu.material.bean.core.BussinessLog; import cn.enilu.material.bean.core.Permission; import cn.enilu.material.bean.core.ShiroUser; import cn.enilu.material.bean.dictmap.UserDict; import cn.enilu.material.bean.dto.UserDto; import cn.enilu.material.bean.entity.system.User; import cn.enilu.material.bean.enumeration.BizExceptionEnum; import cn.enilu.material.bean.exception.ApplicationException; import cn.enilu.material.factory.UserFactory; import cn.enilu.material.service.system.LogObjectHolder; import cn.enilu.material.service.system.UserService; import cn.enilu.material.service.system.impl.ConstantFactory; import cn.enilu.material.shiro.ShiroKit; import cn.enilu.material.utils.BeanUtil; import cn.enilu.material.utils.MD5; import cn.enilu.material.utils.ToolUtil; import cn.enilu.material.warpper.UserWarpper; import com.google.common.base.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.naming.NoPermissionException; import javax.validation.Valid; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; /** * 系统管理员控制器 * * @author fengshuonan * @Date 2017年1月11日 下午1:08:17 */ @Controller @RequestMapping("/mgr") public class UserMgrController extends BaseController { private static String PREFIX = "/system/user/"; @Resource private AppProperties appProperties; @Autowired private UserService userService; /** * 跳转到查看管理员列表的页面 */ @RequestMapping("") public String index() { return PREFIX + "user.html"; } /** * 跳转到查看管理员列表的页面 */ @RequestMapping("/user_add") public String addView() { return PREFIX + "user_add.html"; } /** * 跳转到角色分配页面 */ @Permission @RequestMapping("/role_assign/{userId}") public String roleAssign(@PathVariable Long userId, Model model) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } User user = userService.get(userId); model.addAttribute("userId", userId); model.addAttribute("userAccount", user.getAccount()); return PREFIX + "user_roleassign.html"; } /** * 跳转到编辑管理员页面 */ @Permission @RequestMapping("/user_edit/{userId}") public String userEdit(@PathVariable Long userId, Model model) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } assertAuth(userId); User user = userService.get(userId); model.addAttribute(user); model.addAttribute("roleName", ConstantFactory.me().getRoleName(user.getRoleid())); model.addAttribute("deptName", ConstantFactory.me().getDeptName(user.getDeptid())); LogObjectHolder.me().set(user); return PREFIX + "user_edit.html"; } /** * 跳转到查看用户详情页面 */ @RequestMapping("/user_info") public String userInfo(Model model) { Long userId = ShiroKit.getUser().getId(); if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } User user = userService.get(userId); model.addAttribute(user); model.addAttribute("roleName", ConstantFactory.me().getRoleName(user.getRoleid())); model.addAttribute("deptName", ConstantFactory.me().getDeptName(user.getDeptid())); LogObjectHolder.me().set(user); return PREFIX + "user_view.html"; } /** * 跳转到修改密码界面 */ @RequestMapping("/user_chpwd") public String chPwd() { return PREFIX + "user_chpwd.html"; } /** * 修改当前用户的密码 */ @RequestMapping("/changePwd") @ResponseBody public Object changePwd(@RequestParam String oldPwd, @RequestParam String newPwd, @RequestParam String rePwd) { if (!newPwd.equals(rePwd)) { throw new ApplicationException(BizExceptionEnum.TWO_PWD_NOT_MATCH); } Long userId = ShiroKit.getUser().getId(); User user = userService.get(userId); String oldMd5 = MD5.md5(oldPwd, user.getSalt()); if (user.getPassword().equals(oldMd5)) { String newMd5 = MD5.md5(newPwd, user.getSalt()); user.setPassword(newMd5); userService.update(user); return SUCCESS_TIP; } else { throw new ApplicationException(BizExceptionEnum.OLD_PWD_NOT_RIGHT); } } /** * 查询管理员列表 */ @RequestMapping("/list") @Permission @ResponseBody public Object list(@RequestParam(required = false) String name, @RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) Integer deptid) { Map params = new HashMap<>(); params.put("name", name); params.put("beginTime", beginTime); params.put("endTime", endTime); if (deptid != null && deptid != 0) { params.put("deptid", deptid); } if (!Strings.isNullOrEmpty(name)) { params.put("name","%"+ name+"%"); } List users = userService.findAll(params); return new UserWarpper(BeanUtil.objectsToMaps(users)).warp(); } /** * 添加管理员 */ @RequestMapping("/add") @BussinessLog(value = "添加管理员", key = "account", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip add(@Valid UserDto user, BindingResult result) { if (result.hasErrors()) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } // 判断账号是否重复 User theUser = userService.findByAccount(user.getAccount()); if (theUser != null) { throw new ApplicationException(BizExceptionEnum.USER_ALREADY_REG); } // 完善账号信息 user.setSalt(ToolUtil.getRandomString(5)); user.setPassword(MD5.md5(user.getPassword(), user.getSalt())); user.setStatus(ManagerStatus.OK.getCode()); this.userService.insert(UserFactory.createUser(user, new User())); return SUCCESS_TIP; } /** * 修改管理员 * * @throws NoPermissionException */ @RequestMapping("/edit") @BussinessLog(value = "修改管理员", key = "account", dict = UserDict.class) @ResponseBody @Permission(Const.ADMIN_NAME) public Tip edit(@Valid UserDto user, BindingResult result) throws NoPermissionException { if (result.hasErrors()) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } User oldUser = userService.get(user.getId()); if (ShiroKit.hasRole(Const.ADMIN_NAME)) { userService.update(UserFactory.updateUser(user, oldUser)); return SUCCESS_TIP; } else { assertAuth(user.getId()); ShiroUser shiroUser = ShiroKit.getUser(); if (shiroUser.getId().equals(user.getId())) { userService.update(UserFactory.updateUser(user, oldUser)); return SUCCESS_TIP; } else { throw new ApplicationException(BizExceptionEnum.NO_PERMITION); } } } /** * 删除管理员(逻辑删除) */ @RequestMapping("/delete") @BussinessLog(value = "删除管理员", key = "userId", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip delete(@RequestParam Long userId) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //不能删除超级管理员 if (userId.intValue() == Const.ADMIN_ID) { throw new ApplicationException(BizExceptionEnum.CANT_DELETE_ADMIN); } assertAuth(userId); User user = userService.get(userId); user.setStatus(ManagerStatus.DELETED.getCode()); userService.update(user); return SUCCESS_TIP; } /** * 查看管理员详情 */ @RequestMapping("/view/{userId}") @ResponseBody public User view(@PathVariable Long userId) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } assertAuth(userId); return userService.get(userId); } /** * 重置管理员的密码 */ @RequestMapping("/reset") @BussinessLog(value = "重置管理员密码", key = "userId", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip reset(@RequestParam Long userId) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } if (userId.intValue() == Const.ADMIN_ID) { throw new ApplicationException(BizExceptionEnum.CANT_CHANGE_ADMIN); } assertAuth(userId); User user = userService.get(userId); user.setSalt(ToolUtil.getRandomString(5)); user.setPassword(MD5.md5(Const.DEFAULT_PWD, user.getSalt())); userService.update(user); return SUCCESS_TIP; } /** * 冻结用户 */ @RequestMapping("/freeze") @BussinessLog(value = "冻结用户", key = "userId", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip freeze(@RequestParam Long userId) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //不能冻结超级管理员 if (userId.intValue() == Const.ADMIN_ID) { throw new ApplicationException(BizExceptionEnum.CANT_FREEZE_ADMIN); } assertAuth(userId); User user = userService.get(userId); user.setStatus(ManagerStatus.FREEZED.getCode()); userService.update(user); return SUCCESS_TIP; } /** * 解除冻结用户 */ @RequestMapping("/unfreeze") @BussinessLog(value = "解除冻结用户", key = "userId", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip unfreeze(@RequestParam Long userId) { if (ToolUtil.isEmpty(userId)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } assertAuth(userId); User user = userService.get(userId); user.setStatus(ManagerStatus.OK.getCode()); userService.update(user); return SUCCESS_TIP; } /** * 分配角色 */ @RequestMapping("/setRole") @BussinessLog(value = "分配角色", key = "userId", dict = UserDict.class) @Permission(Const.ADMIN_NAME) @ResponseBody public Tip setRole(@RequestParam("userId") Long userId, @RequestParam("roleIds") String roleIds) { if (ToolUtil.isOneEmpty(userId, roleIds)) { throw new ApplicationException(BizExceptionEnum.REQUEST_NULL); } //不能修改超级管理员 if (userId.intValue() == Const.ADMIN_ID) { throw new ApplicationException(BizExceptionEnum.CANT_CHANGE_ADMIN); } assertAuth(userId); User user = userService.get(userId); user.setRoleid(roleIds); userService.update(user); return SUCCESS_TIP; } /** * 上传图片(上传到项目的webapp/static/img) */ @RequestMapping(method = RequestMethod.POST, path = "/upload") @ResponseBody public String upload(@RequestPart("file") MultipartFile picture) { String pictureName = UUID.randomUUID().toString() + ".jpg"; try { String fileSavePath = appProperties.getFileUploadPath(); picture.transferTo(new File(fileSavePath + pictureName)); } catch (Exception e) { throw new ApplicationException(BizExceptionEnum.UPLOAD_ERROR); } return pictureName; } /** * 判断当前登录的用户是否有操作这个用户的权限 */ private void assertAuth(Long userId) { if (ShiroKit.isAdmin()) { return; } List deptDataScope = ShiroKit.getDeptDataScope(); User user = userService.get(userId); Long deptid = user.getDeptid(); if (deptDataScope.contains(deptid)) { return; } else { throw new ApplicationException(BizExceptionEnum.NO_PERMITION); } } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/transfer/ManagerUser.java ================================================ package cn.enilu.material.admin.modular.system.transfer; import java.util.Date; /** * 管理员的信息封装 * * @author fengshuonan * @Date 2017年1月11日 下午7:46:53 */ public class ManagerUser { private String userId; /* 用户账号 */ private String userNo; /* 用户姓名 */ private String userName; private String userPhone; //1:超级管理员 2:管理员 private String userRole; /* 1:登录状态 2:退出状态 3:停用状态 */ private Integer userStatus; private Date createTime; private Date loginTime; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserNo() { return userNo; } public void setUserNo(String userNo) { this.userNo = userNo; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPhone() { return userPhone; } public void setUserPhone(String userPhone) { this.userPhone = userPhone; } public String getUserRole() { return userRole; } public void setUserRole(String userRole) { this.userRole = userRole; } public Integer getUserStatus() { return userStatus; } public void setUserStatus(Integer userStatus) { this.userStatus = userStatus; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getLoginTime() { return loginTime; } public void setLoginTime(Date loginTime) { this.loginTime = loginTime; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/transfer/ReqAddManager.java ================================================ package cn.enilu.material.admin.modular.system.transfer; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotNull; /** * 添加管理员的请求bean * * @author fengshuonan * @Date 2017年1月12日 下午6:46:24 */ public class ReqAddManager { // 用户姓名 @NotNull private String userName; // 用户账号 @NotNull private String userNo; // 手机号 @NotNull @Length(min = 11, max = 11) private String userPhone; // 1:超级管理员 2:管理员 @NotNull private String userRole; // 密码 @NotNull private String userPassword; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserNo() { return userNo; } public void setUserNo(String userNo) { this.userNo = userNo; } public String getUserPhone() { return userPhone; } public void setUserPhone(String userPhone) { this.userPhone = userPhone; } public String getUserRole() { return userRole; } public void setUserRole(String userRole) { this.userRole = userRole; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/modular/system/transfer/ReqEditManager.java ================================================ package cn.enilu.material.admin.modular.system.transfer; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotNull; /** * 编辑管理员的请求 * * @author fengshuonan * @Date 2017年1月15日 下午10:29:16 */ public class ReqEditManager { @NotNull private String userId; /* 用户姓名 */ @NotNull private String userName; private String userPassword; @NotNull @Length(min = 11, max = 11) private String userPhone; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public String getUserPhone() { return userPhone; } public void setUserPhone(String userPhone) { this.userPhone = userPhone; } } ================================================ FILE: material-manage/src/main/java/cn/enilu/material/admin/runner/StartJob.java ================================================ package cn.enilu.material.admin.runner; import cn.enilu.material.bean.vo.QuartzJob; import cn.enilu.material.service.task.JobService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.util.List; /** * 启动定时任务 * * @author enilu * @Date 2019-08-13 */ @Component public class StartJob implements ApplicationRunner { @Autowired private JobService jobService; private Logger log = LoggerFactory.getLogger(getClass()); @Override public void run(ApplicationArguments applicationArguments) throws Exception { log.info("start Job >>>>>>>>>>>>>>>>>>>>>>>"); List list = jobService.getTaskList(); for (QuartzJob quartzJob : list) { jobService.addJob(quartzJob); } } } ================================================ FILE: material-manage/src/main/resources/META-INF/spring-devtools.properties ================================================ restart.include.beetl=/beetl-2.7.15.jar ================================================ FILE: material-manage/src/main/resources/application-dev.properties ================================================ ## 开发环境配置 spring.datasource.url=jdbc:mysql://enilu-db:3306/material?useUnicode=true&characterEncoding=UTF8&useSSL=false spring.datasource.username=material spring.datasource.password=material@123ABC ##实际开发和生产环境中注释掉下面配置 spring.jpa.hibernate.ddl-auto=create #默认使用InnoDB引擎 spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect spring.datasource.sql-script-encoding=utf-8 spring.jpa.show-sql=true ================================================ FILE: material-manage/src/main/resources/application-lab.properties ================================================ ## 开发环境配置,该配置相对dev增加了个所有实验特性的功能,如果使用改配置,请确保pom.xml添加了material-lab的依赖 spring.datasource.url=jdbc:mysql://enilu-db:3306/material?useUnicode=true&characterEncoding=UTF8&useSSL=false spring.datasource.username=material spring.datasource.password=material@123ABC ##实际开发和生产环境中注释掉下面配置 spring.jpa.hibernate.ddl-auto=create #默认使用InnoDB引擎 spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect spring.datasource.sql-script-encoding=utf-8 spring.jpa.show-sql=true ##################################################################### ##########################以下为实验特性的功能########################### ##################################################################### #启用actuator监控功能 ##监控地址端口 management.server.port=8000 ##springboot2.0之后,在Http环境下将默认的endpoint只设置为info和health,要想开启其他的监控功能,需要手动配置 management.endpoints.web.exposure.include=* ##请求连接前缀 默认是/actuator management.endpoints.web.base-path=/actuator management.health.mail.enabled=false ================================================ FILE: material-manage/src/main/resources/application-prod.properties ================================================ ## 生产环境配置 debug=false #Mysql属性配置文件,Spring-boot系统配置 spring.datasource.url=jdbc:mysql://enilu-db:3306/material?useUnicode=true&characterEncoding=UTF8&useSSL=false spring.datasource.username=material spring.datasource.password=material@123ABC ##实际开发和生产环境中注释掉下面配置 spring.jpa.hibernate.ddl-auto=create #默认使用InnoDB引擎 spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect spring.datasource.sql-script-encoding=utf-8 spring.jpa.show-sql=true ================================================ FILE: material-manage/src/main/resources/application.properties ================================================ #项目环境包括:dev(开发),lab(在dev基础上包含各种实验功能),prod(生产环境) spring.profiles.active=lab server.port=8085 server.tomcat.max-threads=800 spring.main.allow-bean-definition-overriding=true spring.jpa.hibernate.use-new-id-generator-mappings=false ##是否开启swagger (true/false) apps.swagger-open=true #是否开启spring session,如果是多机环境需要开启(true/false) apps.spring-session-open=false #session失效时间(只在单机环境下生效,多机环境在SpringSessionConfig类中配置) 单位:秒 apps.session-invalidate-time=1800 #多久检测一次失效的session(只在单机环境下生效) 单位:秒 apps.session-validation-interval=900 ################### beetl配置 ################### #开始结束标签(yaml不允许@开头) beetl.delimiter-statement-start=@ beetl.delimiter-statement-end=null #自定义标签文件Root目录和后缀 beetl.resource-tagroot=common/tags beetl.resource-tagsuffix=tag #是否检测文件变化,开发用true合适,但线上要改为false beetl.resource-auto-check=true ################### spring配置 ################### spring.mvc.static-path-pattern=/static/** spring.mvc.view.prefix=/WEB-INF/view spring.http.converters.preferred-json-mapper=fastjson #最大请求大小 spring.http.multipart.max-request-size=100MB #最大文件大小 spring.http.multipart.max-file-size=100MB #是否开启开发者工具(true/false) spring.devtools.restart.enabled=false spring.devtools.restart.additional-paths=src/main/java spring.devtools.restart.exclude=static/**,WEB-INF/view/** #false为启用jdk默认动态代理,true为cglib动态代理 spring.aop.proxy-target-class=true spring.datasource.driverClassName=com.mysql.jdbc.Driver ################### 邮件服务配置 ################### spring.mail.host=smtp.qq.com spring.mail.username=eniluzt@qq.com spring.mail.password=peqmfythvisgbhcb spring.mail.port=465 spring.mail.protocol=smtp spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.ssl.enable=true ================================================ FILE: material-manage/src/main/resources/banner.txt ================================================ __ __ _ _____ _____ ____ ___ _ _ _ ____ __ __ ___ _ _ | \/ | / \ |_ _| | ____| | _ \ |_ _| / \ | | / \ | _ \ | \/ | |_ _| | \ | | | |\/| | / _ \ | | | _| | |_) | | | / _ \ | | / _ \ | | | | | |\/| | | | | \| | | | | | / ___ \ | | | |___ | _ < | | / ___ \ | |___ / ___ \ | |_| | | | | | | | | |\ | |_| |_| /_/ \_\ |_| |_____| |_| \_\ |___| /_/ \_\ |_____| /_/ \_\ |____/ |_| |_| |___| |_| \_| by enilu.cn ================================================ FILE: material-manage/src/main/resources/ehcache.xml ================================================ ================================================ FILE: material-manage/src/main/resources/import.sql ================================================ INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('16', '0', '0', '状态', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('17', '1', '16', '启用', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('18', '2', '16', '禁用', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('29', '0', '0', '性别', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('30', '1', '29', '男', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('31', '2', '29', '女', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('35', '0', '0', '账号状态', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('36', '1', '35', '启用', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('37', '2', '35', '冻结', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('38', '3', '35', '已删除', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('53', '0', '0', '证件类型', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('54', '1', '53', '身份证', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('55', '2', '53', '护照', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('68', '0', '0', '是否', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('69', '1', '68', '是', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('70', '0', '68', '否', '2019-01-13 14:18:21', '1', '2019-01-13 14:18:21', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('71', '0', '0', '消息类型', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('72', '0', '71', '短信', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); INSERT INTO `t_sys_dict`(id,value,pid,name,create_time,create_by,modify_time,modify_by) VALUES ('73', '1', '71', '邮件', '2019-07-23 21:30:09', '1', '2019-07-23 21:30:09', '1'); -- ---------------------------- -- Records of t_sys_cfg -- ---------------------------- INSERT INTO `t_sys_cfg` VALUES ('1', null, null, '1', '2019-04-15 21:36:07', '应用名称update by 2019-03-27 11:47:04', 'system.app.name', 'material-admin'); INSERT INTO `t_sys_cfg` VALUES ('2', null, null, '1', '2019-04-15 21:36:17', '系统默认上传文件路径', 'system.file.upload.path', '/data/material-admin/runtime/upload'); INSERT INTO `t_sys_cfg` VALUES ('3', null, null, '1', '2019-04-15 21:36:17', '腾讯sms接口appid', 'api.tencent.sms.appid', '1400219425'); INSERT INTO `t_sys_cfg` VALUES ('4', null, null, '1', '2019-04-15 21:36:17', '腾讯sms接口appkey', 'api.tencent.sms.appkey', '5f71ed5325f3b292946530a1773e997a'); INSERT INTO `t_sys_cfg` VALUES ('5', null, null, '1', '2019-04-15 21:36:17', '腾讯sms接口签名参数', 'api.tencent.sms.sign', '需要去申请咯'); INSERT INTO `t_sys_cfg` VALUES ('6', null, null, '1', '2019-08-18 21:36:17', '系统资源版本号', 'system.resource.version', '1.0'); -- ---------------------------- -- Records of t_sys_dept -- ---------------------------- INSERT INTO `t_sys_dept` VALUES ('1', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '山迪亚集团', '1', '0', '[0],', '总公司', '', null); INSERT INTO `t_sys_dept` VALUES ('2', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '开发部', '2', '1', '[0],[1],', '开发部', null, null); INSERT INTO `t_sys_dept` VALUES ('3', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '运营部', '3', '1', '[0],[1],', '运营部', null, null); INSERT INTO `t_sys_dept` VALUES ('4', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '战略部', '4', '1', '[0],[1],', '战略部', null, null); INSERT INTO `t_sys_dept` VALUES ('5', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '人事部', '5', '1', '[0],[1],', '人事部', null, null); INSERT INTO `t_sys_dept` VALUES ('6', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '行政部', '6', '1', '[0],[1],', '行政部', null, null); INSERT INTO `t_sys_dept` VALUES ('7', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '山迪亚上海有限责任公司', '7', '1', '[0],[1],', '上海分公司', '', null); INSERT INTO `t_sys_dept` VALUES ('8', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '运维部', '8', '7', '[0],[1],[7],', '运维部', null, null); INSERT INTO `t_sys_dept` VALUES ('9', '1', '2019-08-15 16:31:13', '1', '2019-08-15 16:31:13', '销售部', '9', '7', '[0],[1],[7],', '销售部', null, null); -- ---------------------------- -- Records of t_sys_login_log -- ---------------------------- INSERT INTO `t_sys_login_log` (`id`, `logname`, `userid`, `create_time`, `succeed`, `message`, `ip`) VALUES ('71', '登录日志', '1', '2019-05-10 13:17:43', '成功', null, '127.0.0.1'); INSERT INTO `t_sys_login_log` (`id`, `logname`, `userid`, `create_time`, `succeed`, `message`, `ip`) VALUES ('72', '登录日志', '1', '2019-05-12 13:36:56', '成功', null, '127.0.0.1'); -- ---------------------------- -- Records of t_sys_menu -- ---------------------------- INSERT INTO `t_sys_menu` VALUES ('1', null, null, null, null, 'system', 'fa-cog', '1', '1', '1', '系统管理', '1', '0', '[0],', '1', null, '/system'); INSERT INTO `t_sys_menu` VALUES ('4', null, null, '1', '2019-04-16 18:59:15', 'mgr', '', '1', null, '2', '用户管理', '1', 'system', '[0],[system],', '1', null, '/mgr'); INSERT INTO `t_sys_menu` VALUES ('5', null, null, null, null, 'mgr_add', '', '0', null, '3', '添加用户', '1', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/add'); INSERT INTO `t_sys_menu` VALUES ('6', null, null, null, null, 'mgr_edit', '', '0', null, '3', '修改用户', '2', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/edit'); INSERT INTO `t_sys_menu` VALUES ('7', null, null, null, null, 'mgr_delete', '', '0', '0', '3', '删除用户', '3', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/delete'); INSERT INTO `t_sys_menu` VALUES ('8', null, null, null, null, 'mgr_reset', '', '0', '0', '3', '重置密码', '4', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/reset'); INSERT INTO `t_sys_menu` VALUES ('9', null, null, null, null, 'mgr_freeze', '', '0', '0', '3', '冻结用户', '5', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/freeze'); INSERT INTO `t_sys_menu` VALUES ('10', null, null, null, null, 'mgr_unfreeze', '', '0', '0', '3', '解除冻结用户', '6', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/unfreeze'); INSERT INTO `t_sys_menu` VALUES ('11', null, null, null, null, 'mgr_setRole', '', '0', '0', '3', '分配角色', '7', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/setRole'); INSERT INTO `t_sys_menu` VALUES ('12', null, null, null, null, 'role', '', '1', '0', '2', '角色管理', '2', 'system', '[0],[system],', '1', null, '/role'); INSERT INTO `t_sys_menu` VALUES ('13', null, null, null, null, 'role_add', '', '0', '0', '3', '添加角色', '1', 'role', '[0],[system],[role],', '1', null, '/role/add'); INSERT INTO `t_sys_menu` VALUES ('14', null, null, null, null, 'role_edit', '', '0', '0', '3', '修改角色', '2', 'role', '[0],[system],[role],', '1', null, '/role/edit'); INSERT INTO `t_sys_menu` VALUES ('15', null, null, null, null, 'role_remove', '', '0', '0', '3', '删除角色', '3', 'role', '[0],[system],[role],', '1', null, '/role/remove'); INSERT INTO `t_sys_menu` VALUES ('16', null, null, null, null, 'role_setAuthority', '', '0', '0', '3', '配置权限', '4', 'role', '[0],[system],[role],', '1', null, '/role/setAuthority'); INSERT INTO `t_sys_menu` VALUES ('17', null, null, null, null, 'menu', '', '1', '0', '2', '菜单管理', '4', 'system', '[0],[system],', '1', null, '/menu'); INSERT INTO `t_sys_menu` VALUES ('18', null, null, null, null, 'menu_add', '', '0', '0', '3', '添加菜单', '1', 'menu', '[0],[system],[menu],', '1', null, '/menu/add'); INSERT INTO `t_sys_menu` VALUES ('19', null, null, null, null, 'menu_edit', '', '0', '0', '3', '修改菜单', '2', 'menu', '[0],[system],[menu],', '1', null, '/menu/edit'); INSERT INTO `t_sys_menu` VALUES ('20', null, null, null, null, 'menu_remove', '', '0', '0', '3', '删除菜单', '3', 'menu', '[0],[system],[menu],', '1', null, '/menu/remove'); INSERT INTO `t_sys_menu` VALUES ('58', null, null, '47', '2019-06-02 10:25:31', 'log', null, '1', null, '2', '业务日志', '6', 'operationMgr', '[0],[operationMgr],', '1', null, '/log'); INSERT INTO `t_sys_menu` VALUES ('21', null, null, null, null, 'dept', '', '1', null, '2', '部门管理', '3', 'system', '[0],[system],', '1', null, '/dept'); INSERT INTO `t_sys_menu` VALUES ('22', null, null, null, null, 'dict', '', '1', null, '2', '字典管理', '4', 'system', '[0],[system],', '1', null, '/dict'); INSERT INTO `t_sys_menu` VALUES ('59', null, null, '47', '2019-06-02 10:25:36', 'loginLog', null, '1', null, '2', '登录日志', '6', 'operationMgr', '[0],[operationMgr],', '1', null, '/loginLog'); INSERT INTO `t_sys_menu` VALUES ('60', null, null, null, null, 'log_clean', '', '0', null, '3', '清空日志', '3', 'log', '[0],[system],[log],', '1', null, '/log/delLog'); INSERT INTO `t_sys_menu` VALUES ('36', null, null, null, null, 'dept_add', '', '0', null, '3', '添加部门', '1', 'dept', '[0],[system],[dept],', '1', null, '/dept/add'); INSERT INTO `t_sys_menu` VALUES ('23', null, null, null, null, 'dept_update', '', '0', null, '3', '修改部门', '1', 'dept', '[0],[system],[dept],', '1', null, '/dept/update'); INSERT INTO `t_sys_menu` VALUES ('24', null, null, null, null, 'dept_delete', '', '0', null, '3', '删除部门', '1', 'dept', '[0],[system],[dept],', '1', null, '/dept/delete'); INSERT INTO `t_sys_menu` VALUES ('25', null, null, null, null, 'dict_add', '', '0', null, '3', '添加字典', '1', 'dict', '[0],[system],[dict],', '1', null, '/dict/add'); INSERT INTO `t_sys_menu` VALUES ('26', null, null, null, null, 'dict_update', '', '0', null, '3', '修改字典', '1', 'dict', '[0],[system],[dict],', '1', null, '/dict/update'); INSERT INTO `t_sys_menu` VALUES ('27', null, null, null, null, 'dict_delete', '', '0', null, '3', '删除字典', '1', 'dict', '[0],[system],[dict],', '1', null, '/dict/delete'); INSERT INTO `t_sys_menu` VALUES ('28', null, null, null, null, 'to_menu_edit', '', '0', null, '3', '菜单编辑跳转', '4', 'menu', '[0],[system],[menu],', '1', null, '/menu/menu_edit'); INSERT INTO `t_sys_menu` VALUES ('29', null, null, null, null, 'menu_list', '', '0', null, '3', '菜单列表', '5', 'menu', '[0],[system],[menu],', '1', null, '/menu/list'); INSERT INTO `t_sys_menu` VALUES ('30', null, null, null, null, 'to_dept_update', '', '0', null, '3', '修改部门跳转', '4', 'dept', '[0],[system],[dept],', '1', null, '/dept/dept_update'); INSERT INTO `t_sys_menu` VALUES ('31', null, null, null, null, 'dept_list', '', '0', null, '3', '部门列表', '5', 'dept', '[0],[system],[dept],', '1', null, '/dept/list'); INSERT INTO `t_sys_menu` VALUES ('32', null, null, null, null, 'dept_detail', '', '0', null, '3', '部门详情', '6', 'dept', '[0],[system],[dept],', '1', null, '/dept/detail'); INSERT INTO `t_sys_menu` VALUES ('33', null, null, null, null, 'to_dict_edit', '', '0', null, '3', '修改菜单跳转', '4', 'dict', '[0],[system],[dict],', '1', null, '/dict/dict_edit'); INSERT INTO `t_sys_menu` VALUES ('34', null, null, null, null, 'dict_list', '', '0', null, '3', '字典列表', '5', 'dict', '[0],[system],[dict],', '1', null, '/dict/list'); INSERT INTO `t_sys_menu` VALUES ('35', null, null, null, null, 'dict_detail', '', '0', null, '3', '字典详情', '6', 'dict', '[0],[system],[dict],', '1', null, '/dict/detail'); INSERT INTO `t_sys_menu` VALUES ('61', null, null, null, null, 'log_detail', '', '0', null, '3', '日志详情', '3', 'log', '[0],[system],[log],', '1', null, '/log/detail'); INSERT INTO `t_sys_menu` VALUES ('62', null, null, null, null, 'del_login_log', '', '0', null, '3', '清空登录日志', '1', 'loginLog', '[0],[system],[loginLog],', '1', null, '/loginLog/delLoginLog'); INSERT INTO `t_sys_menu` VALUES ('63', null, null, null, null, 'login_log_list', '', '0', null, '3', '登录日志列表', '2', 'loginLog', '[0],[system],[loginLog],', '1', null, '/loginLog/list'); INSERT INTO `t_sys_menu` VALUES ('37', null, null, null, null, 'to_role_edit', '', '0', null, '3', '修改角色跳转', '5', 'role', '[0],[system],[role],', '1', null, '/role/role_edit'); INSERT INTO `t_sys_menu` VALUES ('38', null, null, null, null, 'to_role_assign', '', '0', null, '3', '角色分配跳转', '6', 'role', '[0],[system],[role],', '1', null, '/role/role_assign'); INSERT INTO `t_sys_menu` VALUES ('39', null, null, null, null, 'role_list', '', '0', null, '3', '角色列表', '7', 'role', '[0],[system],[role],', '1', null, '/role/list'); INSERT INTO `t_sys_menu` VALUES ('40', null, null, null, null, 'to_assign_role', '', '0', null, '3', '分配角色跳转', '8', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/role_assign'); INSERT INTO `t_sys_menu` VALUES ('41', null, null, null, null, 'to_user_edit', '', '0', null, '3', '编辑用户跳转', '9', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/user_edit'); INSERT INTO `t_sys_menu` VALUES ('42', null, null, null, null, 'mgr_list', '', '0', null, '3', '用户列表', '10', 'mgr', '[0],[system],[mgr],', '1', null, '/mgr/list'); INSERT INTO `t_sys_menu` VALUES ('43', null, null, null, null, 'cfg', '', '1', null, '2', '参数管理', '10', 'system', '[0],[system],', '1', null, '/cfg'); INSERT INTO `t_sys_menu` VALUES ('44', null, null, null, null, 'cfg_add', '', '0', null, '3', '添加系统参数', '1', 'cfg', '[0],[system],[cfg],', '1', null, '/cfg/add'); INSERT INTO `t_sys_menu` VALUES ('45', null, null, null, null, 'cfg_update', '', '0', null, '3', '修改系统参数', '2', 'cfg', '[0],[system],[cfg],', '1', null, '/cfg/update'); INSERT INTO `t_sys_menu` VALUES ('46', null, null, null, null, 'cfg_delete', '', '0', null, '3', '删除系统参数', '3', 'cfg', '[0],[system],[cfg],', '1', null, '/cfg/delete'); INSERT INTO `t_sys_menu` VALUES ('47', null, null, null, null, 'task', '', '1', null, '2', '任务管理', '11', 'system', '[0],[system],', '1', null, '/task'); INSERT INTO `t_sys_menu` VALUES ('48', null, null, null, null, 'task_add', '', '0', null, '3', '添加任务', '1', 'task', '[0],[system],[task],', '1', null, '/task/add'); INSERT INTO `t_sys_menu` VALUES ('49', null, null, null, null, 'task_update', '', '0', null, '3', '修改任务', '2', 'task', '[0],[system],[task],', '1', null, '/task/update'); INSERT INTO `t_sys_menu` VALUES ('50', null, null, null, null, 'task_delete', '', '0', null, '3', '删除任务', '3', 'task', '[0],[system],[task],', '1', null, '/task/delete'); INSERT INTO `t_sys_menu` VALUES ('3', null, null, '47', '2019-06-02 10:09:09', 'operationMgr', 'fa-wrench', '1', null, '1', '运维管理', '3', '0', '[0],', '1', null, '/optionMgr'); INSERT INTO `t_sys_menu` VALUES ('64', '47', '2019-06-02 10:10:20', '47', '2019-06-02 10:10:20', 'druid', '', '1', null, '2', '数据库监控', '1', 'operationMgr', '[0],[operationMgr],', '1', null, '/druid'); INSERT INTO `t_sys_menu` VALUES ('65', '47', '2019-06-02 10:10:20', '47', '2019-06-02 10:10:20', 'swagger', null, '1', null, '2', '接口文档', '2', 'operationMgr', '[0],[operationMgr],', '1', null, '/swagger-ui.html'); INSERT INTO `t_sys_menu` VALUES ('66', '1', '2019-06-10 21:26:52', '1', '2019-06-10 21:26:52', 'messageMgr', 'fa-envelope-o', '1', null, '1', '消息管理', '2', '0', '[0],', '1', null, '/message'); INSERT INTO `t_sys_menu` VALUES ('67', '1', '2019-06-10 21:27:34', '1', '2019-06-10 21:27:34', 'historyMessage', null, '1', null, '2', '历史消息', '1', 'messageMgr', '[0],[messageMgr],', '1', null, '/message/history'); INSERT INTO `t_sys_menu` VALUES ('68', '1', '2019-06-10 21:27:56', '1', '2019-06-10 21:27:56', 'messageTemplate', null, '1', null, '2', '消息模板', '2', 'messageMgr', '[0],[messageMgr],', '1', null, '/message/template'); INSERT INTO `t_sys_menu` VALUES ('69', '1', '2019-06-10 21:28:21', '1', '2019-06-10 21:28:21', 'messageSender', null, '1', null, '2', '消息发送器', '3', 'messageMgr', '[0],[messageMgr],', '1', null, '/message/sender'); -- ---------------------------- -- Records of t_sys_notice -- ---------------------------- INSERT INTO `t_sys_notice` (`id`, `title`, `type`, `content`, `create_time`, `create_by`, `modify_time`, `modify_by`) VALUES ('1', '世界', '10', '欢迎使用material-admin后台管理系统,点击查看官方文档', '2017-01-11 08:53:20', '1', '2019-01-08 23:30:58', '1'); -- ---------------------------- -- Records of t_sys_operation_log -- ---------------------------- INSERT INTO `t_sys_operation_log` (`id`, `logtype`, `logname`, `userid`, `classname`, `method`, `create_time`, `succeed`, `message`) VALUES ('76', '业务日志', '编辑文章', '1', 'cn.enilu.material.api.controller.cms.ArticleMgrController', 'upload', '2019-05-10 13:22:49', '成功', '名称=null;;;'); INSERT INTO `t_sys_operation_log` (`id`, `logtype`, `logname`, `userid`, `classname`, `method`, `create_time`, `succeed`, `message`) VALUES ('77', '业务日志', '编辑文章', '1', 'cn.enilu.material.api.controller.cms.ArticleMgrController', 'upload', '2019-05-10 13:31:09', '成功', '名称=null;;;'); -- ---------------------------- -- Records of t_sys_relation -- ---------------------------- INSERT INTO `t_sys_relation` VALUES ('1', '1', '1'); INSERT INTO `t_sys_relation` VALUES ('2', '4', '1'); INSERT INTO `t_sys_relation` VALUES ('3', '5', '1'); INSERT INTO `t_sys_relation` VALUES ('4', '6', '1'); INSERT INTO `t_sys_relation` VALUES ('5', '7', '1'); INSERT INTO `t_sys_relation` VALUES ('6', '8', '1'); INSERT INTO `t_sys_relation` VALUES ('7', '9', '1'); INSERT INTO `t_sys_relation` VALUES ('8', '10', '1'); INSERT INTO `t_sys_relation` VALUES ('9', '11', '1'); INSERT INTO `t_sys_relation` VALUES ('10', '40', '1'); INSERT INTO `t_sys_relation` VALUES ('11', '41', '1'); INSERT INTO `t_sys_relation` VALUES ('12', '42', '1'); INSERT INTO `t_sys_relation` VALUES ('13', '12', '1'); INSERT INTO `t_sys_relation` VALUES ('14', '13', '1'); INSERT INTO `t_sys_relation` VALUES ('15', '14', '1'); INSERT INTO `t_sys_relation` VALUES ('16', '15', '1'); INSERT INTO `t_sys_relation` VALUES ('17', '16', '1'); INSERT INTO `t_sys_relation` VALUES ('18', '37', '1'); INSERT INTO `t_sys_relation` VALUES ('19', '38', '1'); INSERT INTO `t_sys_relation` VALUES ('20', '39', '1'); INSERT INTO `t_sys_relation` VALUES ('21', '17', '1'); INSERT INTO `t_sys_relation` VALUES ('22', '18', '1'); INSERT INTO `t_sys_relation` VALUES ('23', '19', '1'); INSERT INTO `t_sys_relation` VALUES ('24', '20', '1'); INSERT INTO `t_sys_relation` VALUES ('25', '28', '1'); INSERT INTO `t_sys_relation` VALUES ('26', '29', '1'); INSERT INTO `t_sys_relation` VALUES ('27', '21', '1'); INSERT INTO `t_sys_relation` VALUES ('28', '23', '1'); INSERT INTO `t_sys_relation` VALUES ('29', '24', '1'); INSERT INTO `t_sys_relation` VALUES ('30', '30', '1'); INSERT INTO `t_sys_relation` VALUES ('31', '31', '1'); INSERT INTO `t_sys_relation` VALUES ('32', '32', '1'); INSERT INTO `t_sys_relation` VALUES ('33', '36', '1'); INSERT INTO `t_sys_relation` VALUES ('34', '22', '1'); INSERT INTO `t_sys_relation` VALUES ('35', '25', '1'); INSERT INTO `t_sys_relation` VALUES ('36', '26', '1'); INSERT INTO `t_sys_relation` VALUES ('37', '27', '1'); INSERT INTO `t_sys_relation` VALUES ('38', '33', '1'); INSERT INTO `t_sys_relation` VALUES ('39', '34', '1'); INSERT INTO `t_sys_relation` VALUES ('40', '35', '1'); INSERT INTO `t_sys_relation` VALUES ('41', '43', '1'); INSERT INTO `t_sys_relation` VALUES ('42', '44', '1'); INSERT INTO `t_sys_relation` VALUES ('43', '45', '1'); INSERT INTO `t_sys_relation` VALUES ('44', '46', '1'); INSERT INTO `t_sys_relation` VALUES ('45', '47', '1'); INSERT INTO `t_sys_relation` VALUES ('46', '48', '1'); INSERT INTO `t_sys_relation` VALUES ('47', '49', '1'); INSERT INTO `t_sys_relation` VALUES ('48', '50', '1'); INSERT INTO `t_sys_relation` VALUES ('49', '2', '1'); INSERT INTO `t_sys_relation` VALUES ('50', '3', '1'); INSERT INTO `t_sys_relation` VALUES ('51', '58', '1'); INSERT INTO `t_sys_relation` VALUES ('52', '60', '1'); INSERT INTO `t_sys_relation` VALUES ('53', '61', '1'); INSERT INTO `t_sys_relation` VALUES ('54', '59', '1'); INSERT INTO `t_sys_relation` VALUES ('55', '62', '1'); INSERT INTO `t_sys_relation` VALUES ('56', '63', '1'); INSERT INTO `t_sys_relation` VALUES ('57', '64', '1'); INSERT INTO `t_sys_relation` VALUES ('58', '65', '1'); INSERT INTO `t_sys_relation` VALUES ('59', '66', '1'); INSERT INTO `t_sys_relation` VALUES ('60', '67', '1'); INSERT INTO `t_sys_relation` VALUES ('61', '68', '1'); INSERT INTO `t_sys_relation` VALUES ('62', '69', '1'); INSERT INTO `t_sys_relation` VALUES ('63', '1', '2'); INSERT INTO `t_sys_relation` VALUES ('64', '4', '2'); INSERT INTO `t_sys_relation` VALUES ('65', '5', '2'); INSERT INTO `t_sys_relation` VALUES ('66', '40', '2'); INSERT INTO `t_sys_relation` VALUES ('67', '41', '2'); INSERT INTO `t_sys_relation` VALUES ('68', '42', '2'); INSERT INTO `t_sys_relation` VALUES ('69', '12', '2'); INSERT INTO `t_sys_relation` VALUES ('70', '13', '2'); INSERT INTO `t_sys_relation` VALUES ('71', '16', '2'); INSERT INTO `t_sys_relation` VALUES ('72', '37', '2'); INSERT INTO `t_sys_relation` VALUES ('73', '38', '2'); INSERT INTO `t_sys_relation` VALUES ('74', '39', '2'); INSERT INTO `t_sys_relation` VALUES ('75', '17', '2'); INSERT INTO `t_sys_relation` VALUES ('76', '28', '2'); INSERT INTO `t_sys_relation` VALUES ('77', '29', '2'); INSERT INTO `t_sys_relation` VALUES ('78', '21', '2'); INSERT INTO `t_sys_relation` VALUES ('79', '23', '2'); INSERT INTO `t_sys_relation` VALUES ('80', '24', '2'); INSERT INTO `t_sys_relation` VALUES ('81', '30', '2'); INSERT INTO `t_sys_relation` VALUES ('82', '31', '2'); INSERT INTO `t_sys_relation` VALUES ('83', '32', '2'); INSERT INTO `t_sys_relation` VALUES ('84', '36', '2'); INSERT INTO `t_sys_relation` VALUES ('85', '22', '2'); INSERT INTO `t_sys_relation` VALUES ('86', '25', '2'); INSERT INTO `t_sys_relation` VALUES ('87', '26', '2'); INSERT INTO `t_sys_relation` VALUES ('88', '27', '2'); INSERT INTO `t_sys_relation` VALUES ('89', '33', '2'); INSERT INTO `t_sys_relation` VALUES ('90', '34', '2'); INSERT INTO `t_sys_relation` VALUES ('91', '35', '2'); INSERT INTO `t_sys_relation` VALUES ('92', '43', '2'); INSERT INTO `t_sys_relation` VALUES ('93', '44', '2'); INSERT INTO `t_sys_relation` VALUES ('94', '45', '2'); INSERT INTO `t_sys_relation` VALUES ('95', '46', '2'); INSERT INTO `t_sys_relation` VALUES ('96', '47', '2'); INSERT INTO `t_sys_relation` VALUES ('97', '48', '2'); INSERT INTO `t_sys_relation` VALUES ('98', '49', '2'); INSERT INTO `t_sys_relation` VALUES ('99', '50', '2'); INSERT INTO `t_sys_relation` VALUES ('100', '3', '2'); INSERT INTO `t_sys_relation` VALUES ('101', '58', '2'); INSERT INTO `t_sys_relation` VALUES ('102', '60', '2'); INSERT INTO `t_sys_relation` VALUES ('103', '61', '2'); INSERT INTO `t_sys_relation` VALUES ('104', '59', '2'); INSERT INTO `t_sys_relation` VALUES ('105', '62', '2'); INSERT INTO `t_sys_relation` VALUES ('106', '63', '2'); INSERT INTO `t_sys_relation` VALUES ('107', '64', '2'); INSERT INTO `t_sys_relation` VALUES ('108', '65', '2'); INSERT INTO `t_sys_relation` VALUES ('109', '66', '2'); INSERT INTO `t_sys_relation` VALUES ('110', '67', '2'); INSERT INTO `t_sys_relation` VALUES ('111', '68', '2'); INSERT INTO `t_sys_relation` VALUES ('112', '69', '2'); -- ---------------------------- -- Records of t_sys_role -- ---------------------------- INSERT INTO `t_sys_role` VALUES ('1', null, null, null, null, '24', '超级管理员', '1', '0', 'administrator', '1'); INSERT INTO `t_sys_role` VALUES ('2', null, null, null, null, '25', '网站管理员', '1', '1', 'developer', null); -- ---------------------------- -- Records of t_sys_task -- ---------------------------- INSERT INTO `t_sys_task` (`id`, `name`, `job_group`, `job_class`, `note`, `cron`, `data`, `exec_at`, `exec_result`, `disabled`, `create_time`, `create_by`, `concurrent`, `modify_time`, `modify_by`) VALUES ('1', '测试任务', 'default', 'cn.enilu.material.service.task.job.HelloJob', '测试任务,每30分钟行一次', '0 0/30 * * * ?', '{\n\"appname\": \"material-admin\",\n\"version\":1\n}\n \n \n \n \n \n \n \n \n \n \n \n ', '2019-03-27 11:47:00', '执行成功', '0', '2018-12-28 09:54:00', '1', '0', '2019-03-27 11:47:11', '-1'); -- ---------------------------- -- Records of t_sys_task_log -- ---------------------------- -- ---------------------------- -- Records of t_sys_user -- ---------------------------- INSERT INTO `t_sys_user` VALUES ('-1', null, null, null, null, 'system', null, null, null, null, '应用系统', null, null, null, null, null, null, null); INSERT INTO `t_sys_user` VALUES ('1', null, '2016-01-29 08:49:53', '1', '2019-03-20 23:45:24', 'admin', 'avatar.png', '2017-05-05 00:00:00', '3', 'eniluzt@qq.com', '管理员', '6ab1f386d715cfb6be85de941d438b02', '15011111111', '1', '8pgby', '2', '1', '25'); INSERT INTO `t_sys_user` VALUES ('2', null, '2018-09-13 17:21:02', '1', '2019-01-09 23:05:51', 'developer', 'avatar.png', '2017-12-31 00:00:00', '4', 'eniluzt@qq.com', '网站管理员', '4552805b07a4bf92ce1cea0373aab868', '15222222222', '2', 'vscp9', '1', '1', null); -- ---------------------------- -- Records of t_test_boy -- ---------------------------- INSERT INTO `t_test_boy` (`id`, `create_by`, `create_time`, `modify_by`, `modify_time`, `age`, `birthday`, `has_girl_friend`, `name`) VALUES ('1', null, null, null, null, '18', '2000-01-01', '1', '张三'); -- ---------------------------- -- Records of t_message_sender -- ---------------------------- INSERT INTO `t_message_sender` VALUES ('1', null, null, null, null, 'tencentSmsSender', ' 腾讯短信服务', null); INSERT INTO `t_message_sender` VALUES ('2', null, null, null, null, 'defaultEmailSender', '默认邮件发送器', null); -- ---------------------------- -- Records of t_message_template -- ---------------------------- INSERT INTO `t_message_template` VALUES ('1', null, null, null, null, 'REGISTER_CODE', '注册页面,点击获取验证码', '【腾讯云】校验码{1},请于5分钟内完成验证,如非本人操作请忽略本短信。', '1', '注册验证码', 0); INSERT INTO `t_message_template` VALUES ('2', null, null, null, null, 'EMAIL_TEST', '测试发送', '你好:{1},欢迎使用{2}', '2', '测试邮件', 1); INSERT INTO `t_message_template` VALUES ('3', null, null, null, null, 'EMAIL_HTML_TEMPLATE_TEST', '测试发送模板邮件', '你好${userName}欢迎使用${appName},这是html模板邮件', '2', '测试发送模板邮件', 1); -- ---------------------------- -- Records of t_message -- ---------------------------- INSERT INTO `t_message` VALUES ('1', null, '2019-06-10 21:20:16', null, null, '【腾讯云】校验码1032,请于5分钟内完成验证,如非本人操作请忽略本短信。', '15021592814', '2', 'REGISTER_CODE', '0'); ================================================ FILE: material-manage/src/main/resources/logback.xml ================================================ admin %d{yyyy-M-d HH:mm:ss} [%thread] %p [%logger{0}]:%L %msg%n UTF-8 /data/app/material-admin/runtime/log/admin_%d{yyyy-MM-dd}.log 1000 %d{yyyy-M-d HH:mm:ss} [%thread] %p [%logger{0}]:%L %msg%n UTF-8 ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/404.html ================================================ Material Admin

    SEX!

    Nah.. it's 404
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/_chat.html ================================================ ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/_footer.html ================================================ ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/_header.html ================================================ ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/_sidebar.html ================================================ ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/include.html ================================================ 后台管理 - 主页
    ${layoutContent}
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/layout.html ================================================ 后台管理 - 主页 @include("/common/_header.html"){}
    @include("/common/_sidebar.html"){} @include("/common/_chat.html"){}
    ${layoutContent}
    @include("/common/_footer.html"){} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/NameCon.tag ================================================ @/* 名称查询条件标签的参数说明: name : 查询条件的名称 id : 查询内容的input框id type: 输入框类型:text,number @*/
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/SelectCon.tag ================================================ @/* 选择查询条件标签的参数说明: name : 查询条件的名称 id : 查询内容的input框id @*/
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/TimeCon.tag ================================================ @/* 时间查询条件标签的参数说明: name : 查询条件的名称 id : 查询内容的input框id type : date 或者 datetime @*/
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/avatar.tag ================================================ @/* 头像参数的说明: name : 名称 id : 头像的id @*/
    @}else{ src="${ctxPath}/kaptcha/${avatarImg}"> @}
     上传
    @if(isNotEmpty(underline) && underline == 'true'){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/button.tag ================================================ @/* 按钮标签中各个参数的说明: btnType : 按钮的类型决定了颜色(default-灰色,primary-绿色,success-蓝色,info-淡蓝色,warning-黄色,danger-红色,white-白色) space : 按钮左侧是否有间隔(true/false) clickFun : 点击按钮所执行的方法 icon : 按钮上的图标的样式 name : 按钮名称 @*/ @var spaceCss = ""; @var btnType = ""; @if(isEmpty(space)){ @ spaceCss = ""; @}else{ @ spaceCss = "button-margin"; @} @if(isEmpty(btnCss)){ @ btnType = "primary"; @}else{ @ btnType = btnCss; @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/datePicker.tag ================================================ @/* 表单中日期选择框框标签中各个参数的说明: id : 元素id name : 元素名称 readonly : readonly属性 placeHolder : placeHolder disabled: 是否禁用 value : 默认值 style : 附加的css属性 id : 查询内容的input框id type : date 或者 datetime @*/
    @if(isNotEmpty(underline) && underline == 'true'){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/input.tag ================================================ @/* 表单中input框标签中各个参数的说明: hidden : input hidden框的id id : input框id name : input框名称 readonly : readonly属性 clickFun : 点击事件的方法名 style : 附加的css属性 @*/
    @if(isNotEmpty(hidden)){ @} @if(isNotEmpty(selectFlag)){ @}
    @if(isNotEmpty(underline) && underline == 'true'){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/select.tag ================================================ @/* select标签中各个参数的说明: name : select的名称 id : select的id underline : 是否带分割线 multiple : 是否多选 @*/
    @if(isNotEmpty(multiple)){ @}else{ @} @if(isNotEmpty(hidden)){ @}
    @if(isNotEmpty(underline) && underline == 'true'){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/table.tag ================================================ @/* 表格标签的参数说明: id : table表格的id @*/
    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/common/tags/textarea.tag ================================================ @/* textarea标签中各个参数的说明: name : 名称 id : id underline : 是否带分割线 rows : 行数 cols : 列数 readonly : 是否只读 @*/
    @if(isNotEmpty(underline) && underline == 'true'){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/index.html ================================================ @layout("/common/layout.html"){

    Sales Statistics Vestibulum purus quam scelerisque, mollis nonummy metus

    Website Traffics

    987,459

    Website Impressions

    356,785K

    Total Sales

    $ 458,778

    Support Tickets

    23,856

    Page Views

    47,896,536


    Site Visitors

    24,456,799


    Total Clicks

    13,965

    Email Statistics
    45
    Total Emails Sent
    56
    Bounce Rate
    84
    Total Opened

    Todo List

    Add, edit and manage your Todo Lists
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/lab/actuator.html ================================================ @layout("/common/layout.html"){

    实验室

    这里会列出本系统集成的一些实验性质的功能,都放在material-lab模块里,通过启用配置文件application-lab.properties来启用这些功能


    - 使用actuator可以方便的对spring boot应用做监控
    - 启用该功能后 访问http://localhost:8000/actuator/env
    
    测试访问:http://localhost:8000/actuator/env
    更多监控接口查看http://localhost:8000/actuator
    acturator提供的监控能力有限,而且没有界面,如果需要更成熟的工具,可以参考试试:Spring Boot Admin
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/lab/gis.html ================================================ @layout("/common/layout.html"){

    实验室

    这里会列出本系统集成的一些实验性质的功能,都放在material-lab模块里,通过启用配置文件application-lab.properties来启用这些功能


    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/login.html ================================================ flash-material - 登录

    请输入账户的注册邮箱,以找回密码

    ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/history/message.html ================================================ @layout("/common/layout.html"){

    历史消息列表

    <#table id="MessageTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/history/message_view.html ================================================ @layout("/common/include.html"){
    <#input name="消息模板" value="${item.tplCode}" readonly="readonly"/>
    <#input name="接收人" value="${item.receiver}" underline="true" readonly="readonly"/>
    <#textarea name="消息内容" value="${item.content}" readonly="readonly" rows="3" cols="50"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/sender/sender.html ================================================ @layout("/common/layout.html"){

    发送器列表

    <#table id="MessageSenderTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/sender/sender_add.html ================================================ @layout("/common/include.html"){
    <#input id="name" name="名称"/>
    <#input id="className" name="类名" underline="true"/>
    <#input id="tplCode" name="外部模板编号" underline="true"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="MessageSenderInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="MessageSenderInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/sender/sender_edit.html ================================================ @layout("/common/include.html"){
    <#input id="id" name="ID" value="${item.id}" underline="true" readonly="readonly"/>
    <#input id="name" name="名称" value="${item.name}" />
    <#input id="className" name="类名" value="${item.className}" underline="true"/>
    <#input id="tplCode" name="外部模板编号" value="${item.tplCode}" />
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="MessageSenderInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="MessageSenderInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/template/template.html ================================================ @layout("/common/layout.html"){

    模板列表

    <#table id="MessageTemplateTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/template/template_add.html ================================================ @layout("/common/include.html"){
    <#input id="code" name="编号"/>
    <#input id="title" name="标题"/>
    <#select id="type" name="消息类型"> @for(dict in constant.getDicts('消息类型')){ @}
    <#select id="idMessageSender" name="发送器"> @for(dict in messageSenderList){ @}
    <#textarea id="cond" name="发送条件" rows="5" cols="50"/>
    <#textarea id="content" name="内容" rows="5" cols="50"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="MessageTemplateInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="MessageTemplateInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/message/template/template_edit.html ================================================ @layout("/common/include.html"){
    <#input id="code" name="编号" value="${item.code}"/>
    <#input id="title" name="标题" value="${item.title}"/>
    <#select id="type" name="消息类型"> @for(dict in constant.getDicts('消息类型')){ @}
    <#select id="idMessageSender" name="发送器"> @for(sender in messageSenderList){ @}
    <#textarea id="cond" name="发送条件" rows="5" cols="50" value="${item.cond}"/>
    <#textarea id="content" name="内容" rows="5" cols="50" value="${item.content}"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="MessageTemplateInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="MessageTemplateInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/cfg/cfg.html ================================================ @layout("/common/layout.html"){

    参数管理

    <#table id="CfgTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/cfg/cfg_add.html ================================================ @layout("/common/include.html"){
    <#input id="cfgName" name="参数名"/> <#input id="cfgValue" name="参数值" underline="true"/>
    <#input id="cfgDesc" name="参数描述" underline="true"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="CfgInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="CfgInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/cfg/cfg_edit.html ================================================ @layout("/common/include.html"){
    <#input id="id" name="自增主键" value="${item.id}" disabled="disabled"/> <#input id="cfgName" name="参数名" value="${item.cfgName}" />
    <#input id="cfgValue" name="参数值" value="${item.cfgValue}" underline="true"/> <#input id="cfgDesc" name="参数描述" value="${item.cfgDesc}" />
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="CfgInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="CfgInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dept/dept.html ================================================ @layout("/common/layout.html"){

    部门管理

    <#table id="DeptTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dept/dept_add.html ================================================ @layout("/common/include.html"){
    <#input id="simplename" name="部门名称" underline="true"/> <#input id="fullname" name="部门全称" underline="true"/> <#input id="tips" name="备注" underline="true"/>
    <#input id="num" name="排序" underline="true"/> <#input id="pName" name="上级部门" readonly="readonly" hidden="pid" clickFun="DeptInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="DeptInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="DeptInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dept/dept_edit.html ================================================ @layout("/common/include.html"){
    <#input id="simplename" name="部门名称" underline="true" value="${dept.simplename}"/> <#input id="fullname" name="部门全称" underline="true" value="${dept.fullname}"/> <#input id="tips" name="备注" underline="true" value="${dept.tips}"/>
    <#input id="num" name="排序" underline="true" value="${dept.num}"/> <#input id="pName" name="上级部门" readonly="readonly" hidden="pid" hiddenValue="${dept.pid}" value="${pName}" clickFun="DeptInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="DeptInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="DeptInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dict/dict.html ================================================ @layout("/common/layout.html"){

    字典管理

    <#table id="DictTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dict/dict_add.html ================================================ @layout("/common/include.html"){
    <#button btnCss="info" name="增加" icon="fa-plus" clickFun="DictInfoDlg.addItem()"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="DictInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="DictInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/dict/dict_edit.html ================================================ @layout("/common/include.html"){
    <#button btnCss="info" name="增加" icon="fa-plus" clickFun="DictInfoDlg.addItem()"/>
    @for(item in subDicts){
    <#button name="删除" icon="fa-remove" btnCss="danger" id="cancel" clickFun="DictInfoDlg.deleteItem(event)"/>
    @}
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="DictInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="DictInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/menu/menu.html ================================================ @layout("/common/layout.html"){

    菜单管理

    <#table id="menuTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/menu/menu_add.html ================================================ @layout("/common/include.html"){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/menu/menu_edit.html ================================================ @layout("/common/include.html"){
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/role/role.html ================================================ @layout("/common/layout.html"){

    角色管理

    <#table id="roleTable"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/role/role_add.html ================================================ @layout("/common/include.html"){
    <#input id="name" name="角色名称" underline="true"/> <#input id="pName" name="上级名称" underline="true" hidden="pid" readonly="readonly" clickFun="RolInfoDlg.showPNameSelectTree(); return false;" style="background-color: #ffffff !important;"/> <#input id="deptName" name="部门名称" hidden="deptid" readonly="readonly" clickFun="RolInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/>
    <#input id="tips" name="别名" underline="true"/> <#input id="num" name="排序" type="number"/>
    <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="RolInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="RolInfoDlg.close()"/>
    @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/role/role_assign.html ================================================ @layout("/common/include.html"){

    ${roleName!}

       
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/role/role_edit.html ================================================ @layout("/common/include.html"){
      <#input id="name" name="角色名称" underline="true" value="${role.name}"/> <#input id="pName" name="上级名称" underline="true" hidden="pid" hiddenValue="${role.pid}" readonly="readonly" value="${pName}" clickFun="RolInfoDlg.showPNameSelectTree(); return false;" style="background-color: #ffffff !important;"/> <#input id="deptName" name="部门名称" hidden="deptid" hiddenValue="${role.deptid}" readonly="readonly" value="${deptName}" clickFun="RolInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/>
      <#input id="tips" name="别名" underline="true" value="${role.tips}"/> <#input id="num" name="排序" value="${role.num}"/>
      <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="RolInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="RolInfoDlg.close()"/>
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/task/task.html ================================================ @layout("/common/layout.html"){

      任务管理

      <#table id="TaskTable"/>
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/task/task_add.html ================================================ @layout("/common/include.html"){
      <#input id="name" name="任务名"/> <#textarea id="jobClass" name="执行类" underline="true" rows="3" cols="50"/> <#textarea id="data" name="执行参数" underline="true" rows="3" cols="50"/>
      <#input id="cron" name="定时规则"/> <#textarea id="note" name="任务说明" underline="true" rows="3" cols="50"/>
      <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="TaskInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="TaskInfoDlg.close()"/>
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/task/task_edit.html ================================================ @layout("/common/include.html"){
      <#input id="id" name="自增主键" value="${item.id}" disabled="disabled"/> <#input id="name" name="任务名" value="${item.name}" underline="true" /> <#textarea id="jobClass" name="执行类" value="${item.jobClass}" underline="true" rows="3" cols="50"/> <#textarea id="data" name="执行参数" value="${item.data}" underline="true" rows="3" cols="50"/>
      <#input id="cron" name="定时规则" value="${item.cron}" /> <#textarea id="note" name="任务说明" value="${item.note}" rows="3" cols="50"/>
      <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="TaskInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="TaskInfoDlg.close()"/>
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/task/task_log.html ================================================ @layout("/common/include.html"){
      <#table id="TaskLogTable"/>
      @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user.html ================================================ @layout("/common/layout.html"){

      用户管理

      组织机构
        <#table id="managerTable"/>
        @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user_add.html ================================================ @layout("/common/include.html"){
        <#input id="account" name="账户" underline="false"/> <#select id="sex" name="性别" underline="false"> @for(dict in constant.getDicts('性别')){ @} <#input id="password" name="密码" underline="false" type="password"/> <#input id="roleid" name="角色" underline="false" disabled="disabled"/> <#input id="email" name="邮箱" type="email"/>
        <#input id="name" name="姓名" underline="false"/> <#datePicker id="birthday" name="出生日期" underline="false" type="date"/> <#input id="rePassword" name="确认密码" type="password" underline="false"/> <#input id="citySel" name="部门" underline="true" readonly="readonly" hidden="deptid" clickFun="UserInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/> <#input id="phone" name="电话"/>
        <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="UserInfoDlg.addSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="UserInfoDlg.close()"/>
        @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user_chpwd.html ================================================ @layout("/common/layout.html"){

        个人资料

        <#input id="oldPwd" name="原密码" underline="true" type="password"/> <#input id="newPwd" name="新密码" underline="true" type="password"/> <#input id="rePwd" name="新密码验证" type="password"/>
        <#button btnCss="primary" name="提 交" id="ensure" icon="fa-check" clickFun="UserInfoDlg.chPwd()"/>
        @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user_edit.html ================================================ @layout("/common/include.html"){
        <#input id="account" name="账户" underline="true" value="${user.account}"/> <#select id="sex" name="性别" underline="true"> @for(dict in constant.getDicts('性别')){ @} <#input id="roleid" name="角色" underline="true" value="${roleName}" disabled="disabled"/> <#input id="email" name="邮箱" type="email" value="${user.email}"/>
        <#input id="name" name="姓名" underline="true" value="${user.name}"/> <#input id="birthday" name="出生日期" underline="true" type="date" value="${user.birthday}" clickFun="laydate({istime: false, format: 'YYYY-MM-DD'})" /> <#input id="citySel" name="部门" underline="true" readonly="readonly" hidden="deptid" hiddenValue="${user.deptid}" value="${deptName}" clickFun="UserInfoDlg.showDeptSelectTree(); return false;" style="background-color: #ffffff !important;"/> <#input id="phone" name="电话" value="${user.phone}"/>
        <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="UserInfoDlg.editSubmit()"/> <#button btnCss="danger" name="取消" id="cancel" icon="fa-eraser" clickFun="UserInfoDlg.close()"/>
        @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user_roleassign.html ================================================ @layout("/common/include.html"){
           
          @} ================================================ FILE: material-manage/src/main/webapp/WEB-INF/view/system/user/user_view.html ================================================ @layout("/common/layout.html"){

          个人资料

          <#avatar id="avatar" name="头像" underline="true" avatarImg="${user.avatar}"/> <#input id="account" name="账户" underline="true" value="${user.account}" disabled="disabled" /> <#select id="sex" name="性别" underline="true" value="${user.sex}"> <#input id="roleid" name="角色" underline="true" value="${roleName}" disabled="disabled"/> <#input id="email" name="邮箱" type="email" value="${user.email}"/>
          <#input id="name" name="姓名" underline="true" value="${user.name}"/> <#datePicker id="birthday" name="出生日期" value="${user.birthday}"/> <#input id="citySel" name="部门" underline="true" readonly="readonly" value="${deptName}" hidden="deptid" hiddenValue="${user.deptid}" clickFun="UserInfoDlg.showInfoDeptSelectTree(); return false;" style="background-color: #ffffff !important;" selectFlag="true" selectId="menuContent" selectTreeId="treeDemo" selectStyle="width:250px !important;"/> <#input id="phone" name="电话" value="${user.phone}"/>
          <#button btnCss="info" name="提交" id="ensure" icon="fa-check" clickFun="UserInfoDlg.editSubmit()"/>
          @} ================================================ FILE: material-manage/src/main/webapp/static/css/_fstyle.css ================================================ .button-margin { margin-left: 15px !important; } .input-none-margin { margin: 0px !important; } .btn-margin-left { margin-left: 15px !important; } .table-head { float: left; width: 100%; height: auto; } .head-scu-label { margin-top: 35px; } .head-scu-btn { margin-top: 68px; } .line-margin { margin: 8px 0 !important; } .be-driver-checkbox { margin-top: 7px; } .btn-group-m-t { margin-top: 20px; } .upload-btn { white-space: nowrap; } .tree-box { border-radius: 0px !important; box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2) !important; background: rgb(250, 250, 250) none repeat scroll 0% 0% !important; border: 1px solid rgb(204, 204, 204) !important; overflow-y: scroll !important; overflow-x: auto !important; margin-top: 0px !important; width: 224px !important; max-height: 160px !important; -moz-user-select: none !important; } .dept-tree { padding:10px } .w-e-text-container{ height: 150px !important; } .editorHeight{ height: 170px; } ================================================ FILE: material-manage/src/main/webapp/static/css/app.css ================================================ /* * Load Main Bootstrap LESS files */ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { font-size: 2em; margin: 0.67em 0; } mark { background: #ff0; color: #000; } small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { box-sizing: content-box; height: 0; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { color: inherit; font: inherit; margin: 0; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } input[type="search"] { -webkit-appearance: textfield; box-sizing: content-box; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } legend { border: 0; padding: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { *, *:before, *:after { background: transparent !important; color: #000 !important; box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="#"]:after, a[href^="javascript:"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .navbar { display: none; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table td, .table th { background-color: #fff !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .glyphicon-asterisk:before { content: "\002a"; } .glyphicon-plus:before { content: "\002b"; } .glyphicon-euro:before, .glyphicon-eur:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .glyphicon-cd:before { content: "\e201"; } .glyphicon-save-file:before { content: "\e202"; } .glyphicon-open-file:before { content: "\e203"; } .glyphicon-level-up:before { content: "\e204"; } .glyphicon-copy:before { content: "\e205"; } .glyphicon-paste:before { content: "\e206"; } .glyphicon-alert:before { content: "\e209"; } .glyphicon-equalizer:before { content: "\e210"; } .glyphicon-king:before { content: "\e211"; } .glyphicon-queen:before { content: "\e212"; } .glyphicon-pawn:before { content: "\e213"; } .glyphicon-bishop:before { content: "\e214"; } .glyphicon-knight:before { content: "\e215"; } .glyphicon-baby-formula:before { content: "\e216"; } .glyphicon-tent:before { content: "\26fa"; } .glyphicon-blackboard:before { content: "\e218"; } .glyphicon-bed:before { content: "\e219"; } .glyphicon-apple:before { content: "\f8ff"; } .glyphicon-erase:before { content: "\e221"; } .glyphicon-hourglass:before { content: "\231b"; } .glyphicon-lamp:before { content: "\e223"; } .glyphicon-duplicate:before { content: "\e224"; } .glyphicon-piggy-bank:before { content: "\e225"; } .glyphicon-scissors:before { content: "\e226"; } .glyphicon-bitcoin:before { content: "\e227"; } .glyphicon-btc:before { content: "\e227"; } .glyphicon-xbt:before { content: "\e227"; } .glyphicon-yen:before { content: "\00a5"; } .glyphicon-jpy:before { content: "\00a5"; } .glyphicon-ruble:before { content: "\20bd"; } .glyphicon-rub:before { content: "\20bd"; } .glyphicon-scale:before { content: "\e230"; } .glyphicon-ice-lolly:before { content: "\e231"; } .glyphicon-ice-lolly-tasted:before { content: "\e232"; } .glyphicon-education:before { content: "\e233"; } .glyphicon-option-horizontal:before { content: "\e234"; } .glyphicon-option-vertical:before { content: "\e235"; } .glyphicon-menu-hamburger:before { content: "\e236"; } .glyphicon-modal-window:before { content: "\e237"; } .glyphicon-oil:before { content: "\e238"; } .glyphicon-grain:before { content: "\e239"; } .glyphicon-sunglasses:before { content: "\e240"; } .glyphicon-text-size:before { content: "\e241"; } .glyphicon-text-color:before { content: "\e242"; } .glyphicon-text-background:before { content: "\e243"; } .glyphicon-object-align-top:before { content: "\e244"; } .glyphicon-object-align-bottom:before { content: "\e245"; } .glyphicon-object-align-horizontal:before { content: "\e246"; } .glyphicon-object-align-left:before { content: "\e247"; } .glyphicon-object-align-vertical:before { content: "\e248"; } .glyphicon-object-align-right:before { content: "\e249"; } .glyphicon-triangle-right:before { content: "\e250"; } .glyphicon-triangle-left:before { content: "\e251"; } .glyphicon-triangle-bottom:before { content: "\e252"; } .glyphicon-triangle-top:before { content: "\e253"; } .glyphicon-console:before { content: "\e254"; } .glyphicon-superscript:before { content: "\e255"; } .glyphicon-subscript:before { content: "\e256"; } .glyphicon-menu-left:before { content: "\e257"; } .glyphicon-menu-right:before { content: "\e258"; } .glyphicon-menu-down:before { content: "\e259"; } .glyphicon-menu-up:before { content: "\e260"; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: roboto; font-size: 13px; line-height: 1.42857143; color: #5e5e5e; background-color: #edecec; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #2196f3; text-decoration: none; } a:hover, a:focus { color: #0a6ebd; text-decoration: none; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; max-width: 100%; height: auto; } .img-rounded { border-radius: 2px; } .img-thumbnail { padding: 3px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #ededed; border-radius: 2px; -webkit-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; display: inline-block; max-width: 100%; height: auto; } .img-circle { border-radius: 50%; } hr { margin-top: 18px; margin-bottom: 18px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } [role="button"] { cursor: pointer; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: #000000; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #777777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 18px; margin-bottom: 9px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 9px; margin-bottom: 9px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 33px; } h2, .h2 { font-size: 27px; } h3, .h3 { font-size: 23px; } h4, .h4 { font-size: 17px; } h5, .h5 { font-size: 13px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 9px; } .lead { margin-bottom: 18px; font-size: 14px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 19.5px; } } small, .small { font-size: 92%; } mark, .mark { background-color: #ffa829; padding: .2em; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777777; } .text-primary { color: #2196f3; } a.text-primary:hover, a.text-primary:focus { color: #0c7cd5; } .text-success { color: #67bd6a; } a.text-success:hover, a.text-success:focus { color: #49a84d; } .text-info { color: #31708f; } a.text-info:hover, a.text-info:focus { color: #245269; } .text-warning { color: #ffa829; } a.text-warning:hover, a.text-warning:focus { color: #f59200; } .text-danger { color: #f6675d; } a.text-danger:hover, a.text-danger:focus { color: #f33a2c; } .bg-primary { color: #fff; background-color: #2196f3; } a.bg-primary:hover, a.bg-primary:focus { background-color: #0c7cd5; } .bg-success { background-color: #67bd6a; } a.bg-success:hover, a.bg-success:focus { background-color: #49a84d; } .bg-info { background-color: #d9edf7; } a.bg-info:hover, a.bg-info:focus { background-color: #afd9ee; } .bg-warning { background-color: #ffa829; } a.bg-warning:hover, a.bg-warning:focus { background-color: #f59200; } .bg-danger { background-color: #f6675d; } a.bg-danger:hover, a.bg-danger:focus { background-color: #f33a2c; } .page-header { padding-bottom: 8px; margin: 36px 0 18px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 9px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; margin-left: -5px; } .list-inline > li { display: inline-block; padding-left: 5px; padding-right: 5px; } dl { margin-top: 0; margin-bottom: 18px; } dt, dd { line-height: 1.42857143; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; clear: left; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 9px 18px; margin: 0 0 18px; font-size: 16.25px; border-left: 5px solid #eeeeee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; text-align: right; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } address { margin-bottom: 18px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 2px; } kbd { padding: 2px 4px; font-size: 90%; color: #ffffff; background-color: #333333; border-radius: 2px; box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; box-shadow: none; } pre { display: block; padding: 8.5px; margin: 0 0 9px; font-size: 12px; line-height: 1.42857143; word-break: break-all; word-wrap: break-word; color: #333333; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 2px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } @media (min-width: 768px) { .container { width: 100%; } } @media (min-width: 992px) { .container { width: 100%; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } .row { margin-left: -15px; margin-right: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-left: 15px; padding-right: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0%; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0%; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0%; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0%; } } table { background-color: #ffffff; } caption { padding-top: 10px; padding-bottom: 10px; color: #777777; text-align: left; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 18px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 10px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #f0f0f0; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #f0f0f0; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #f0f0f0; } .table .table { background-color: #edecec; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 7px; } .table-bordered { border: 1px solid #f0f0f0; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #f0f0f0; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-of-type(odd) { background-color: #f4f4f4; } .table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { position: static; float: none; display: table-column; } table td[class*="col-"], table th[class*="col-"] { position: static; float: none; display: table-cell; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #fffcbe; } .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #fffba4; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #67bd6a; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #55b559; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #ffa829; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #ff9e0f; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f6675d; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #f55145; } .table-responsive { overflow-x: auto; min-height: 0.01%; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 13.5px; overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #f0f0f0; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { padding: 0; margin: 0; border: 0; min-width: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 18px; font-size: 19.5px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } output { display: block; padding-top: 7px; font-size: 13px; line-height: 1.42857143; color: #000000; } .form-control { display: block; width: 100%; height: 35px; padding: 6px 12px; font-size: 13px; line-height: 1.42857143; color: #000000; background-color: #ffffff; background-image: none; border: 1px solid #e0e0e0; border-radius: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #b4b4b4; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(180, 180, 180, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(180, 180, 180, 0.6); } .form-control::-moz-placeholder { color: #999999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control::-ms-expand { border: 0; background-color: transparent; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: #eeeeee; opacity: 1; } .form-control[disabled], fieldset[disabled] .form-control { cursor: not-allowed; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { line-height: 35px; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { line-height: 30px; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { line-height: 40px; } } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { min-height: 18px; padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-left: -20px; margin-top: 4px \9; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { position: relative; display: inline-block; padding-left: 20px; margin-bottom: 0; vertical-align: middle; font-weight: normal; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; min-height: 31px; } .form-control-static.input-lg, .form-control-static.input-sm { padding-left: 0; padding-right: 0; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm, select[multiple].input-sm { height: auto; } .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .form-group-sm select.form-control { height: 30px; line-height: 30px; } .form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control { height: auto; } .form-group-sm .form-control-static { height: 30px; min-height: 30px; padding: 6px 10px; font-size: 12px; line-height: 1.5; } .input-lg { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } select.input-lg { height: 40px; line-height: 40px; } textarea.input-lg, select[multiple].input-lg { height: auto; } .form-group-lg .form-control { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } .form-group-lg select.form-control { height: 40px; line-height: 40px; } .form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control { height: auto; } .form-group-lg .form-control-static { height: 40px; min-height: 35px; padding: 11px 16px; font-size: 17px; line-height: 1.3333333; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 43.75px; } .form-control-feedback { position: absolute; top: 0; right: 0; z-index: 2; display: block; width: 35px; height: 35px; line-height: 35px; text-align: center; pointer-events: none; } .input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback { width: 40px; height: 40px; line-height: 40px; } .input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { color: #67bd6a; } .has-success .form-control { border-color: #67bd6a; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #49a84d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #aedcb0; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #aedcb0; } .has-success .input-group-addon { color: #67bd6a; border-color: #67bd6a; background-color: #67bd6a; } .has-success .form-control-feedback { color: #67bd6a; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { color: #ffa829; } .has-warning .form-control { border-color: #ffa829; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #f59200; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffd28f; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffd28f; } .has-warning .input-group-addon { color: #ffa829; border-color: #ffa829; background-color: #ffa829; } .has-warning .form-control-feedback { color: #ffa829; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { color: #f6675d; } .has-error .form-control { border-color: #f6675d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #f33a2c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #fbc2bd; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #fbc2bd; } .has-error .input-group-addon { color: #f6675d; border-color: #f6675d; background-color: #f6675d; } .has-error .form-control-feedback { color: #f6675d; } .has-feedback label ~ .form-control-feedback { top: 23px; } .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #9e9e9e; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .form-control-static { display: inline-block; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { margin-top: 0; margin-bottom: 0; padding-top: 7px; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 25px; } .form-horizontal .form-group { margin-left: -15px; margin-right: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; margin-bottom: 0; padding-top: 7px; } } .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 11px; font-size: 17px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; font-size: 12px; } } .btn { display: inline-block; margin-bottom: 0; font-weight: 400; text-align: center; vertical-align: middle; touch-action: manipulation; cursor: pointer; background-image: none; border: 1px solid transparent; white-space: nowrap; padding: 6px 12px; font-size: 13px; line-height: 1.42857143; border-radius: 2px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } .btn:hover, .btn:focus, .btn.focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { outline: 0; background-image: none; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { cursor: not-allowed; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } a.btn.disabled, fieldset[disabled] a.btn { pointer-events: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:focus, .btn-default.focus { color: #333333; background-color: #e6e6e6; border-color: #8c8c8c; } .btn-default:hover { color: #333333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { color: #333333; background-color: #d4d4d4; border-color: #8c8c8c; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #333333; } .btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .open > .dropdown-toggle.btn-default { color: #333333; background-color: #ffffff; border-color: transparent; } .btn-default:hover:hover, .btn-default:focus:hover, .btn-default.focus:hover, .btn-default:active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:hover:focus, .btn-default:focus:focus, .btn-default.focus:focus, .btn-default:active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:hover.focus, .btn-default:focus.focus, .btn-default.focus.focus, .btn-default:active.focus, .open > .dropdown-toggle.btn-default.focus { color: #333333; background-color: #ffffff; border-color: transparent; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #333333; } .btn-primary { color: #ffffff; background-color: #2196f3; border-color: #0d8aee; } .btn-primary:focus, .btn-primary.focus { color: #ffffff; background-color: #0c7cd5; border-color: #064475; } .btn-primary:hover { color: #ffffff; background-color: #0c7cd5; border-color: #0a68b4; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #ffffff; background-color: #0c7cd5; border-color: #0a68b4; } .btn-primary:active:hover, .btn-primary.active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:active:focus, .btn-primary.active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:active.focus, .btn-primary.active.focus, .open > .dropdown-toggle.btn-primary.focus { color: #ffffff; background-color: #0a68b4; border-color: #064475; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus { background-color: #2196f3; border-color: #0d8aee; } .btn-primary .badge { color: #2196f3; background-color: #ffffff; } .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .open > .dropdown-toggle.btn-primary { color: #ffffff; background-color: #2196f3; border-color: transparent; } .btn-primary:hover:hover, .btn-primary:focus:hover, .btn-primary.focus:hover, .btn-primary:active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:hover:focus, .btn-primary:focus:focus, .btn-primary.focus:focus, .btn-primary:active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:hover.focus, .btn-primary:focus.focus, .btn-primary.focus.focus, .btn-primary:active.focus, .open > .dropdown-toggle.btn-primary.focus { color: #ffffff; background-color: #2196f3; border-color: transparent; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active { background-color: #2196f3; border-color: #0d8aee; } .btn-primary .badge { color: #2196f3; background-color: #ffffff; } .btn-success { color: #ffffff; background-color: #4caf50; border-color: #449d48; } .btn-success:focus, .btn-success.focus { color: #ffffff; background-color: #3d8b40; border-color: #1e441f; } .btn-success:hover { color: #ffffff; background-color: #3d8b40; border-color: #327334; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #ffffff; background-color: #3d8b40; border-color: #327334; } .btn-success:active:hover, .btn-success.active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:active:focus, .btn-success.active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:active.focus, .btn-success.active.focus, .open > .dropdown-toggle.btn-success.focus { color: #ffffff; background-color: #327334; border-color: #1e441f; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus { background-color: #4caf50; border-color: #449d48; } .btn-success .badge { color: #4caf50; background-color: #ffffff; } .btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .open > .dropdown-toggle.btn-success { color: #ffffff; background-color: #4caf50; border-color: transparent; } .btn-success:hover:hover, .btn-success:focus:hover, .btn-success.focus:hover, .btn-success:active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:hover:focus, .btn-success:focus:focus, .btn-success.focus:focus, .btn-success:active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:hover.focus, .btn-success:focus.focus, .btn-success.focus.focus, .btn-success:active.focus, .open > .dropdown-toggle.btn-success.focus { color: #ffffff; background-color: #4caf50; border-color: transparent; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active { background-color: #4caf50; border-color: #449d48; } .btn-success .badge { color: #4caf50; background-color: #ffffff; } .btn-info { color: #ffffff; background-color: #00bcd4; border-color: #00a5bb; } .btn-info:focus, .btn-info.focus { color: #ffffff; background-color: #008fa1; border-color: #00343b; } .btn-info:hover { color: #ffffff; background-color: #008fa1; border-color: #006f7d; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #ffffff; background-color: #008fa1; border-color: #006f7d; } .btn-info:active:hover, .btn-info.active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:active:focus, .btn-info.active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { color: #ffffff; background-color: #006f7d; border-color: #00343b; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus { background-color: #00bcd4; border-color: #00a5bb; } .btn-info .badge { color: #00bcd4; background-color: #ffffff; } .btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .open > .dropdown-toggle.btn-info { color: #ffffff; background-color: #00bcd4; border-color: transparent; } .btn-info:hover:hover, .btn-info:focus:hover, .btn-info.focus:hover, .btn-info:active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:hover:focus, .btn-info:focus:focus, .btn-info.focus:focus, .btn-info:active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:hover.focus, .btn-info:focus.focus, .btn-info.focus.focus, .btn-info:active.focus, .open > .dropdown-toggle.btn-info.focus { color: #ffffff; background-color: #00bcd4; border-color: transparent; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active { background-color: #00bcd4; border-color: #00a5bb; } .btn-info .badge { color: #00bcd4; background-color: #ffffff; } .btn-warning { color: #ffffff; background-color: #ff9800; border-color: #e68900; } .btn-warning:focus, .btn-warning.focus { color: #ffffff; background-color: #cc7a00; border-color: #663d00; } .btn-warning:hover { color: #ffffff; background-color: #cc7a00; border-color: #a86400; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #ffffff; background-color: #cc7a00; border-color: #a86400; } .btn-warning:active:hover, .btn-warning.active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:active:focus, .btn-warning.active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:active.focus, .btn-warning.active.focus, .open > .dropdown-toggle.btn-warning.focus { color: #ffffff; background-color: #a86400; border-color: #663d00; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus { background-color: #ff9800; border-color: #e68900; } .btn-warning .badge { color: #ff9800; background-color: #ffffff; } .btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .open > .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ff9800; border-color: transparent; } .btn-warning:hover:hover, .btn-warning:focus:hover, .btn-warning.focus:hover, .btn-warning:active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:hover:focus, .btn-warning:focus:focus, .btn-warning.focus:focus, .btn-warning:active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:hover.focus, .btn-warning:focus.focus, .btn-warning.focus.focus, .btn-warning:active.focus, .open > .dropdown-toggle.btn-warning.focus { color: #ffffff; background-color: #ff9800; border-color: transparent; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active { background-color: #ff9800; border-color: #e68900; } .btn-warning .badge { color: #ff9800; background-color: #ffffff; } .btn-danger { color: #ffffff; background-color: #f44336; border-color: #f32c1e; } .btn-danger:focus, .btn-danger.focus { color: #ffffff; background-color: #ea1c0d; border-color: #891008; } .btn-danger:hover { color: #ffffff; background-color: #ea1c0d; border-color: #c8180b; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #ffffff; background-color: #ea1c0d; border-color: #c8180b; } .btn-danger:active:hover, .btn-danger.active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:active:focus, .btn-danger.active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:active.focus, .btn-danger.active.focus, .open > .dropdown-toggle.btn-danger.focus { color: #ffffff; background-color: #c8180b; border-color: #891008; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus { background-color: #f44336; border-color: #f32c1e; } .btn-danger .badge { color: #f44336; background-color: #ffffff; } .btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .open > .dropdown-toggle.btn-danger { color: #ffffff; background-color: #f44336; border-color: transparent; } .btn-danger:hover:hover, .btn-danger:focus:hover, .btn-danger.focus:hover, .btn-danger:active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:hover:focus, .btn-danger:focus:focus, .btn-danger.focus:focus, .btn-danger:active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:hover.focus, .btn-danger:focus.focus, .btn-danger.focus.focus, .btn-danger:active.focus, .open > .dropdown-toggle.btn-danger.focus { color: #ffffff; background-color: #f44336; border-color: transparent; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active { background-color: #f44336; border-color: #f32c1e; } .btn-danger .badge { color: #f44336; background-color: #ffffff; } .btn-link { color: #2196f3; font-weight: normal; border-radius: 0; } .btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #0a6ebd; text-decoration: none; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #777777; text-decoration: none; } .btn-lg, .btn-group-lg > .btn { padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 2px; } .btn-sm, .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .btn-xs, .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; -o-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } tr.collapse.in { display: table-row; } tbody.collapse.in { display: table-row-group; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition-property: height, visibility; transition-property: height, visibility; -webkit-transition-duration: 0.35s; transition-duration: 0.35s; -webkit-transition-timing-function: ease; transition-timing-function: ease; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px dashed; border-top: 4px solid \9; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropup, .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 9; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; list-style: none; font-size: 13px; text-align: left; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 8px 0; overflow: hidden; background-color: rgba(0, 0, 0, 0.08); } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.42857143; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { text-decoration: none; color: #333333; background-color: rgba(0, 0, 0, 0.075); } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #333333; text-decoration: none; outline: 0; background-color: rgba(0, 0, 0, 0.075); } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #e4e4e4; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); cursor: not-allowed; } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-menu-right { left: auto; right: 0; } .dropdown-menu-left { left: 0; right: auto; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.42857143; color: #777777; white-space: nowrap; } .dropdown-backdrop { position: fixed; left: 0; right: 0; bottom: 0; top: 0; z-index: -1; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px dashed; border-bottom: 4px solid \9; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 2px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { left: auto; right: 0; } .navbar-right .dropdown-menu-left { left: 0; right: auto; } } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar { margin-left: -5px; } .btn-toolbar .btn, .btn-toolbar .btn-group, .btn-toolbar .input-group { float: left; } .btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-bottom-right-radius: 0; border-top-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-top-right-radius: 0; } .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .btn + .dropdown-toggle { padding-left: 8px; padding-right: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-left: 12px; padding-right: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 2px; border-top-left-radius: 2px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-right-radius: 0; border-top-left-radius: 0; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { float: none; display: table-cell; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn input[type="radio"], [data-toggle="buttons"] > .btn-group > .btn input[type="radio"], [data-toggle="buttons"] > .btn input[type="checkbox"], [data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-left: 0; padding-right: 0; } .input-group .form-control { position: relative; z-index: 2; float: left; width: 100%; margin-bottom: 0; } .input-group .form-control:focus { z-index: 3; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 40px; line-height: 40px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 13px; font-weight: normal; line-height: 1; color: #000000; text-align: center; background-color: transparent; border: 1px solid transparent; border-radius: 0; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 2px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 17px; border-radius: 0px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-top-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { border-bottom-left-radius: 0; border-top-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; font-size: 0; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -1px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { z-index: 2; } .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { margin-right: -1px; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { z-index: 2; margin-left: -1px; } .nav { margin-bottom: 0; padding-left: 0; list-style: none; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li.disabled > a { color: #777777; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #777777; text-decoration: none; background-color: transparent; cursor: not-allowed; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eeeeee; border-color: #2196f3; } .nav .nav-divider { height: 1px; margin: 8px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #ffffff; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.42857143; border: 1px solid transparent; border-radius: 2px 2px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #ffffff; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555555; background-color: transparent; border: 1px solid #ffffff; border-bottom-color: transparent; cursor: default; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { text-align: center; margin-bottom: 5px; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 2px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 2px 2px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #edecec; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 2px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #ffffff; background-color: #2196f3; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { text-align: center; margin-bottom: 5px; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 2px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 2px 2px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #edecec; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 18px; border: 1px solid transparent; } @media (min-width: 768px) { .navbar { border-radius: 2px; } } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { overflow-x: visible; padding-right: 15px; padding-left: 15px; border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -webkit-overflow-scrolling: touch; } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-left: 0; padding-right: 0; } } .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } @media (max-device-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; } } .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; padding: 16px 15px; font-size: 17px; line-height: 18px; height: 50px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } .navbar-brand > img { display: block; } @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; margin-right: 15px; padding: 9px 10px; margin-top: 8px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 2px; } .navbar-toggle:focus { outline: 0; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 8px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 18px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 18px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 16px; padding-bottom: 16px; } } .navbar-form { margin-left: -15px; margin-right: -15px; padding: 10px 15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); margin-top: 7.5px; margin-bottom: 7.5px; } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; width: auto; vertical-align: middle; } .navbar-form .form-control-static { display: inline-block; } .navbar-form .input-group { display: inline-table; vertical-align: middle; } .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { width: auto; } .navbar-form .input-group > .form-control { width: 100%; } .navbar-form .control-label { margin-bottom: 0; vertical-align: middle; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .navbar-form .radio label, .navbar-form .checkbox label { padding-left: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .navbar-form .has-feedback .form-control-feedback { top: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } .navbar-form .form-group:last-child { margin-bottom: 0; } } @media (min-width: 768px) { .navbar-form { width: auto; border: 0; margin-left: 0; margin-right: 0; padding-top: 0; padding-bottom: 0; -webkit-box-shadow: none; box-shadow: none; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { margin-bottom: 0; border-top-right-radius: 2px; border-top-left-radius: 2px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-btn { margin-top: 7.5px; margin-bottom: 7.5px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 16px; margin-bottom: 16px; } @media (min-width: 768px) { .navbar-text { float: left; margin-left: 15px; margin-right: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: -15px; } .navbar-right ~ .navbar-right { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777777; } .navbar-default .navbar-nav > li > a { color: #777777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #cccccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #dddddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #dddddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #888888; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { background-color: #e7e7e7; color: #555555; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #cccccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777777; } .navbar-default .navbar-link:hover { color: #333333; } .navbar-default .btn-link { color: #777777; } .navbar-default .btn-link:hover, .navbar-default .btn-link:focus { color: #333333; } .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { color: #cccccc; } .navbar-inverse { background-color: #222222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #9d9d9d; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-text { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #ffffff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { background-color: #080808; color: #ffffff; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #9d9d9d; } .navbar-inverse .navbar-link:hover { color: #ffffff; } .navbar-inverse .btn-link { color: #9d9d9d; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { color: #ffffff; } .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { color: #444444; } .breadcrumb { padding: 8px 20px; margin-bottom: 18px; list-style: none; background-color: transparent; border-radius: 2px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { content: "/\00a0"; padding: 0 5px; color: #cccccc; } .breadcrumb > .active { color: #7c7c7c; } .pagination { display: inline-block; padding-left: 0; margin: 18px 0; border-radius: 2px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; line-height: 1.42857143; text-decoration: none; color: #7e7e7e; background-color: #e2e2e2; border: 1px solid #ffffff; margin-left: -1px; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { z-index: 2; color: #333333; background-color: #d7d7d7; border-color: #ffffff; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 3; color: #ffffff; background-color: #00bcd4; border-color: #ffffff; cursor: default; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #777777; background-color: #e2e2e2; border-color: #ffffff; cursor: not-allowed; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 17px; line-height: 1.3333333; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; line-height: 1.5; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pager { padding-left: 0; margin: 18px 0; list-style: none; text-align: center; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #e2e2e2; border: 1px solid #ffffff; border-radius: 5px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #d7d7d7; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #777777; background-color: #e2e2e2; cursor: not-allowed; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #2196f3; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #0c7cd5; } .label-success { background-color: #4caf50; } .label-success[href]:hover, .label-success[href]:focus { background-color: #3d8b40; } .label-info { background-color: #00bcd4; } .label-info[href]:hover, .label-info[href]:focus { background-color: #008fa1; } .label-warning { background-color: #ff9800; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #cc7a00; } .label-danger { background-color: #f44336; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #ea1c0d; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: 400; color: #ffffff; line-height: 1; vertical-align: middle; white-space: nowrap; text-align: center; background-color: #2196f3; border-radius: 2px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .btn-xs .badge, .btn-group-xs > .btn .badge { top: 0; padding: 1px 5px; } a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #ffffff; background-color: #2196f3; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding-top: 30px; padding-bottom: 30px; margin-bottom: 30px; color: inherit; background-color: #f7f7f7; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 20px; font-weight: 200; } .jumbotron > hr { border-top-color: #dedede; } .container .jumbotron, .container-fluid .jumbotron { border-radius: 2px; padding-left: 15px; padding-right: 15px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron, .container-fluid .jumbotron { padding-left: 60px; padding-right: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 59px; } } .thumbnail { display: block; padding: 3px; margin-bottom: 18px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #ededed; border-radius: 2px; -webkit-transition: border 0.2s ease-in-out; -o-transition: border 0.2s ease-in-out; transition: border 0.2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-left: auto; margin-right: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #2196f3; } .thumbnail .caption { padding: 9px; color: #5e5e5e; } .alert { padding: 15px; margin-bottom: 18px; border: 1px solid transparent; border-radius: 2px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { background-color: rgba(76, 175, 80, 0.7); border-color: transparent; color: #ffffff; } .alert-success hr { border-top-color: rgba(0, 0, 0, 0); } .alert-success .alert-link { color: #e6e6e6; } .alert-info { background-color: rgba(33, 150, 243, 0.7); border-color: transparent; color: #ffffff; } .alert-info hr { border-top-color: rgba(0, 0, 0, 0); } .alert-info .alert-link { color: #e6e6e6; } .alert-warning { background-color: rgba(255, 193, 7, 0.7); border-color: transparent; color: #ffffff; } .alert-warning hr { border-top-color: rgba(0, 0, 0, 0); } .alert-warning .alert-link { color: #e6e6e6; } .alert-danger { background-color: rgba(244, 67, 54, 0.7); border-color: transparent; color: #ffffff; } .alert-danger hr { border-top-color: rgba(0, 0, 0, 0); } .alert-danger .alert-link { color: #e6e6e6; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { overflow: hidden; height: 18px; margin-bottom: 18px; background-color: #f5f5f5; border-radius: 2px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress-bar { float: left; width: 0%; height: 100%; font-size: 12px; line-height: 18px; color: #ffffff; text-align: center; background-color: #2196f3; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-transition: width 0.6s ease; -o-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .progress-bar, .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: 40px 40px; } .progress.active .progress-bar, .progress-bar.active { -webkit-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #4caf50; } .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #00bcd4; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #ff9800; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #f44336; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media, .media-body { zoom: 1; overflow: hidden; } .media-body { width: 10000px; } .media-object { display: block; } .media-object.img-thumbnail { max-width: none; } .media-right, .media > .pull-right { padding-left: 10px; } .media-left, .media > .pull-left { padding-right: 10px; } .media-left, .media-right, .media-body { display: table-cell; vertical-align: top; } .media-middle { vertical-align: middle; } .media-bottom { vertical-align: bottom; } .media-heading { margin-top: 0; margin-bottom: 5px; } .media-list { padding-left: 0; list-style: none; } .list-group { margin-bottom: 20px; padding-left: 0; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #ffffff; border: 1px solid #e9e9e9; } .list-group-item:first-child { border-top-right-radius: 2px; border-top-left-radius: 2px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; } a.list-group-item, button.list-group-item { color: #555555; } a.list-group-item .list-group-item-heading, button.list-group-item .list-group-item-heading { color: #333333; } a.list-group-item:hover, button.list-group-item:hover, a.list-group-item:focus, button.list-group-item:focus { text-decoration: none; color: #555555; background-color: #f5f5f5; } button.list-group-item { width: 100%; text-align: left; } .list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { background-color: #ffffff; color: #b5b4b4; cursor: not-allowed; } .list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { color: inherit; } .list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { color: #b5b4b4; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #000000; background-color: #f5f5f5; border-color: #e9e9e9; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #ffffff; } .list-group-item-success { color: #67bd6a; background-color: #67bd6a; } a.list-group-item-success, button.list-group-item-success { color: #67bd6a; } a.list-group-item-success .list-group-item-heading, button.list-group-item-success .list-group-item-heading { color: inherit; } a.list-group-item-success:hover, button.list-group-item-success:hover, a.list-group-item-success:focus, button.list-group-item-success:focus { color: #67bd6a; background-color: #55b559; } a.list-group-item-success.active, button.list-group-item-success.active, a.list-group-item-success.active:hover, button.list-group-item-success.active:hover, a.list-group-item-success.active:focus, button.list-group-item-success.active:focus { color: #fff; background-color: #67bd6a; border-color: #67bd6a; } .list-group-item-info { color: #31708f; background-color: #d9edf7; } a.list-group-item-info, button.list-group-item-info { color: #31708f; } a.list-group-item-info .list-group-item-heading, button.list-group-item-info .list-group-item-heading { color: inherit; } a.list-group-item-info:hover, button.list-group-item-info:hover, a.list-group-item-info:focus, button.list-group-item-info:focus { color: #31708f; background-color: #c4e3f3; } a.list-group-item-info.active, button.list-group-item-info.active, a.list-group-item-info.active:hover, button.list-group-item-info.active:hover, a.list-group-item-info.active:focus, button.list-group-item-info.active:focus { color: #fff; background-color: #31708f; border-color: #31708f; } .list-group-item-warning { color: #ffa829; background-color: #ffa829; } a.list-group-item-warning, button.list-group-item-warning { color: #ffa829; } a.list-group-item-warning .list-group-item-heading, button.list-group-item-warning .list-group-item-heading { color: inherit; } a.list-group-item-warning:hover, button.list-group-item-warning:hover, a.list-group-item-warning:focus, button.list-group-item-warning:focus { color: #ffa829; background-color: #ff9e0f; } a.list-group-item-warning.active, button.list-group-item-warning.active, a.list-group-item-warning.active:hover, button.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus, button.list-group-item-warning.active:focus { color: #fff; background-color: #ffa829; border-color: #ffa829; } .list-group-item-danger { color: #f6675d; background-color: #f6675d; } a.list-group-item-danger, button.list-group-item-danger { color: #f6675d; } a.list-group-item-danger .list-group-item-heading, button.list-group-item-danger .list-group-item-heading { color: inherit; } a.list-group-item-danger:hover, button.list-group-item-danger:hover, a.list-group-item-danger:focus, button.list-group-item-danger:focus { color: #f6675d; background-color: #f55145; } a.list-group-item-danger.active, button.list-group-item-danger.active, a.list-group-item-danger.active:hover, button.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus, button.list-group-item-danger.active:focus { color: #fff; background-color: #f6675d; border-color: #f6675d; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 18px; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 15px; color: inherit; } .panel-title > a, .panel-title > small, .panel-title > .small, .panel-title > small > a, .panel-title > .small > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .list-group, .panel > .panel-collapse > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { padding-left: 15px; padding-right: 15px; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { border-top-left-radius: 1px; border-top-right-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 1px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { border-bottom-left-radius: 1px; border-bottom-right-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 1px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { border-top: 1px solid #f0f0f0; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { border: 0; margin-bottom: 0; } .panel-group { margin-bottom: 18px; } .panel-group .panel { margin-bottom: 0; border-radius: 2px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse > .panel-body, .panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-default { border-color: #dddddd; } .panel-default > .panel-heading { color: #333333; background-color: #f5f5f5; border-color: #dddddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #dddddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #dddddd; } .panel-primary { border-color: #2196f3; } .panel-primary > .panel-heading { color: #ffffff; background-color: #2196f3; border-color: #2196f3; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #2196f3; } .panel-primary > .panel-heading .badge { color: #2196f3; background-color: #ffffff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #2196f3; } .panel-success { border-color: #61b555; } .panel-success > .panel-heading { color: #67bd6a; background-color: #67bd6a; border-color: #61b555; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #61b555; } .panel-success > .panel-heading .badge { color: #67bd6a; background-color: #67bd6a; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #61b555; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #ff760f; } .panel-warning > .panel-heading { color: #ffa829; background-color: #ffa829; border-color: #ff760f; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ff760f; } .panel-warning > .panel-heading .badge { color: #ffa829; background-color: #ffa829; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ff760f; } .panel-danger { border-color: #f54556; } .panel-danger > .panel-heading { color: #f6675d; background-color: #f6675d; border-color: #f54556; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #f54556; } .panel-danger > .panel-heading .badge { color: #f6675d; background-color: #f6675d; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #f54556; } .embed-responsive { position: relative; display: block; height: 0; padding: 0; overflow: hidden; } .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video { position: absolute; top: 0; left: 0; bottom: 0; height: 100%; width: 100%; border: 0; } .embed-responsive-16by9 { padding-bottom: 56.25%; } .embed-responsive-4by3 { padding-bottom: 75%; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 2px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-lg { padding: 24px; border-radius: 2px; } .well-sm { padding: 9px; border-radius: 2px; } .close { float: right; font-size: 19.5px; font-weight: bold; line-height: 1; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.5; filter: alpha(opacity=50); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .modal-open { overflow: hidden; } .modal { display: none; overflow: hidden; position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; -webkit-overflow-scrolling: touch; outline: 0; } .modal.fade .modal-dialog { -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); -o-transform: translate(0, -25%); transform: translate(0, -25%); -webkit-transition: -webkit-transform 0.3s ease-out; -moz-transition: -moz-transform 0.3s ease-out; -o-transition: -o-transform 0.3s ease-out; transition: transform 0.3s ease-out; } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); -o-transform: translate(0, 0); transform: translate(0, 0); } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); background-clip: padding-box; outline: 0; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 11; background-color: #000000; } .modal-backdrop.fade { opacity: 0; filter: alpha(opacity=0); } .modal-backdrop.in { opacity: 0.5; filter: alpha(opacity=50); } .modal-header { padding: 15px; border-bottom: 1px solid transparent; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: transparent; } .modal-body { position: relative; padding: 15px; } .modal-footer { padding: 15px; text-align: right; border-top: 1px solid transparent; } .modal-footer .btn + .btn { margin-left: 5px; margin-bottom: 0; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } .modal-sm { width: 300px; } } @media (min-width: 992px) { .modal-lg { width: 900px; } } .tooltip { position: absolute; z-index: 1070; display: block; font-family: roboto; font-style: normal; font-weight: normal; letter-spacing: normal; line-break: auto; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; white-space: normal; word-break: normal; word-spacing: normal; word-wrap: normal; font-size: 12px; opacity: 0; filter: alpha(opacity=0); } .tooltip.in { opacity: 1; filter: alpha(opacity=100); } .tooltip.top { margin-top: -3px; padding: 5px 0; } .tooltip.right { margin-left: 3px; padding: 0 5px; } .tooltip.bottom { margin-top: 3px; padding: 5px 0; } .tooltip.left { margin-left: -3px; padding: 0 5px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; background-color: #737373; border-radius: 2px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.top-left .tooltip-arrow { bottom: 0; right: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.top-right .tooltip-arrow { bottom: 0; left: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-width: 5px 5px 5px 0; border-right-color: #737373; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-width: 5px 0 5px 5px; border-left-color: #737373; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .tooltip.bottom-left .tooltip-arrow { top: 0; right: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .tooltip.bottom-right .tooltip-arrow { top: 0; left: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .popover { position: absolute; top: 0; left: 0; z-index: 9; display: none; max-width: 276px; padding: 1px; font-family: roboto; font-style: normal; font-weight: normal; letter-spacing: normal; line-break: auto; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; white-space: normal; word-break: normal; word-spacing: normal; word-wrap: normal; font-size: 13px; background-color: #ffffff; background-clip: padding-box; border: 1px solid #ffffff; border-radius: 2px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { margin: 0; padding: 8px 14px; font-size: 13px; background-color: #ffffff; border-bottom: 1px solid #f2f2f2; border-radius: 1px 1px 0 0; } .popover-content { padding: 9px 14px; } .popover > .arrow, .popover > .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover > .arrow { border-width: 11px; } .popover > .arrow:after { border-width: 10px; content: ""; } .popover.top > .arrow { left: 50%; margin-left: -11px; border-bottom-width: 0; border-top-color: #cccccc; border-top-color: #ffffff; bottom: -11px; } .popover.top > .arrow:after { content: " "; bottom: 1px; margin-left: -10px; border-bottom-width: 0; border-top-color: #ffffff; } .popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; border-left-width: 0; border-right-color: #cccccc; border-right-color: #ffffff; } .popover.right > .arrow:after { content: " "; left: 1px; bottom: -10px; border-left-width: 0; border-right-color: #ffffff; } .popover.bottom > .arrow { left: 50%; margin-left: -11px; border-top-width: 0; border-bottom-color: #cccccc; border-bottom-color: #ffffff; top: -11px; } .popover.bottom > .arrow:after { content: " "; top: 1px; margin-left: -10px; border-top-width: 0; border-bottom-color: #ffffff; } .popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; border-right-width: 0; border-left-color: #cccccc; border-left-color: #ffffff; } .popover.left > .arrow:after { content: " "; right: 1px; border-right-width: 0; border-left-color: #ffffff; bottom: -10px; } .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; } .carousel-inner > .item { display: none; position: relative; -webkit-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } @media all and (transform-3d), (-webkit-transform-3d) { .carousel-inner > .item { -webkit-transition: -webkit-transform 0.6s ease-in-out; -moz-transition: -moz-transform 0.6s ease-in-out; -o-transition: -o-transform 0.6s ease-in-out; transition: transform 0.6s ease-in-out; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; perspective: 1000px; } .carousel-inner > .item.next, .carousel-inner > .item.active.right { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); left: 0; } .carousel-inner > .item.prev, .carousel-inner > .item.active.left { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); left: 0; } .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); left: 0; } } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: 15%; opacity: 0.5; filter: alpha(opacity=50); font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0); } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { left: auto; right: 0; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { outline: 0; color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; margin-top: -10px; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; line-height: 1; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid #ffffff; border-radius: 10px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); } .carousel-indicators .active { margin: 0; width: 12px; height: 12px; background-color: #ffffff; } .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -10px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -10px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -10px; } .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-header:before, .modal-header:after, .modal-footer:before, .modal-footer:after { content: " "; display: table; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-header:after, .modal-footer:after { clear: both; } .center-block { display: block; margin-left: auto; margin-right: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table !important; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table !important; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table !important; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table !important; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table !important; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } /* * LESS Plugins */ /* * Variable and Mixin */ /* * Font Icon Family */ /* * Grid System */ /* Typography + Scaffolding + Links */ /* Border Radius */ /* Tabs */ /* Form */ /* Table */ /* * Input Group */ /* * Pagination */ /* * Popover */ /* * Dropdown */ /* * Thumbnail */ /* * Alerts */ /* * Form Validations */ /* * Buttons */ /* * Thumbnail */ /* * Carousel */ /* * Modal */ /* * Tooltips */ /* * Popover */ /* * Breadcrumbs */ /* * Jumbotron */ /* * List Groups */ /* * Badges */ /* * Material Colors */ /* Bootstrap Branding */ /* * Colors */ /* * Blocks */ /* * Misc */ /* * Font Face */ /* * Background Repeat + Position */ /* * CSS Animations based on animate.css */ /* * CSS Transform - Scale and Rotate */ /* * User Select */ /* * Background Image Cover */ /* * Tab Focus */ /* * Pop-in Hover effects */ /* * Override Bootstrap Button Mixin */ /* * Scale 3d */ /* * Load Font */ /* * Roboto Light */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Light-webfont.eot'); src: url('../fonts/roboto/Roboto-Light-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Light-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Light-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Light-webfont.svg#icon') format('svg'); font-weight: 300; font-style: normal; } /* * Roboto Regular */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Regular-webfont.eot'); src: url('../fonts/roboto/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Regular-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Regular-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Regular-webfont.svg#icon') format('svg'); font-weight: 400; font-style: normal; } /* * Roboto Medium */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Medium-webfont.eot'); src: url('../fonts/roboto/Roboto-Medium-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Medium-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Medium-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Medium-webfont.svg#icon') format('svg'); font-weight: 500; font-style: normal; } /* * Roboto Bold */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Bold-webfont.eot'); src: url('../fonts/roboto/Roboto-Bold-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Bold-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Bold-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Bold-webfont.svg#icon') format('svg'); font-weight: 700; font-style: normal; } /* * Shadow Light */ @font-face { font-family: shadowsintolight; src: url('../fonts/shadowsintolight/shadowsintolight-webfont.eot'); src: url('../fonts/shadowsintolight/shadowsintolight-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/shadowsintolight/shadowsintolight-webfont.woff') format('woff'), url('../fonts/shadowsintolight/shadowsintolight-webfont.ttf') format('truetype'), url('../fonts/shadowsintolight/shadowsintolight-webfont.svg#icon') format('svg'); font-weight: 400; font-style: normal; } /* * Vendors */ @font-face { font-family: weather-icons; src: url('../fonts/weather-icons/weather-icons.eot'); src: url('../fonts/weather-icons/weather-icons.eot?#iefix') format('embedded-opentype'), url('../fonts/weather-icons/weather-icons.woff') format('woff'), url('../fonts/weather-icons/weather-icons.ttf') format('truetype'), url('../fonts/weather-icons/weather-icons.svg#icon') format('svg'); font-weight: 400; font-style: normal; } #weather-widget [class*="icon-"] { font-family: 'weather-icons'; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-0:before { content: ":"; } .icon-1:before { content: "p"; } .icon-2:before { content: "S"; } .icon-3:before { content: "Q"; } .icon-4:before { content: "S"; } .icon-5:before { content: "W"; } .icon-6:before { content: "W"; } .icon-7:before { content: "W"; } .icon-8:before { content: "W"; } .icon-9:before { content: "I"; } .icon-10:before { content: "W"; } .icon-11:before { content: "I"; } .icon-12:before { content: "I"; } .icon-13:before { content: "I"; } .icon-14:before { content: "I"; } .icon-15:before { content: "W"; } .icon-16:before { content: "I"; } .icon-17:before { content: "W"; } .icon-18:before { content: "U"; } .icon-19:before { content: "Z"; } .icon-20:before { content: "Z"; } .icon-21:before { content: "Z"; } .icon-22:before { content: "Z"; } .icon-23:before { content: "Z"; } .icon-24:before { content: "E"; } .icon-25:before { content: "E"; } .icon-26:before { content: "3"; } .icon-27:before { content: "a"; } .icon-28:before { content: "A"; } .icon-29:before { content: "a"; } .icon-30:before { content: "A"; } .icon-31:before { content: "6"; } .icon-32:before { content: "1"; } .icon-33:before { content: "6"; } .icon-34:before { content: "1"; } .icon-35:before { content: "W"; } .icon-36:before { content: "1"; } .icon-37:before { content: "S"; } .icon-38:before { content: "S"; } .icon-39:before { content: "S"; } .icon-40:before { content: "M"; } .icon-41:before { content: "W"; } .icon-42:before { content: "I"; } .icon-43:before { content: "W"; } .icon-44:before { content: "a"; } .icon-45:before { content: "S"; } .icon-46:before { content: "U"; } .icon-47:before { content: "S"; } .btn-file { overflow: hidden; position: relative; vertical-align: middle; } .btn-file > input { position: absolute; top: 0; right: 0; margin: 0; opacity: 0; filter: alpha(opacity=0); font-size: 23px; height: 100%; width: 100%; direction: ltr; cursor: pointer; } .fileinput { margin-bottom: 9px; display: inline-block; } .fileinput .form-control { padding-top: 7px; padding-bottom: 5px; display: inline-block; margin-bottom: 0px; vertical-align: middle; cursor: text; } .fileinput .thumbnail { overflow: hidden; display: inline-block; margin-bottom: 5px; vertical-align: middle; text-align: center; } .fileinput .thumbnail > img { max-height: 100%; } .fileinput .btn { vertical-align: middle; } .fileinput-exists .fileinput-new, .fileinput-new .fileinput-exists { display: none; } .fileinput-inline .fileinput-controls { display: inline; } .fileinput-filename { vertical-align: middle; display: inline-block; overflow: hidden; } .form-control .fileinput-filename { vertical-align: bottom; } .fileinput.input-group { display: table; } .fileinput.input-group > * { position: relative; z-index: 2; } .fileinput.input-group > .btn-file { z-index: 1; } .fileinput-new.input-group .btn-file, .fileinput-new .input-group .btn-file { border-radius: 0 2px 2px 0; } .fileinput-new.input-group .btn-file.btn-xs, .fileinput-new .input-group .btn-file.btn-xs, .fileinput-new.input-group .btn-file.btn-sm, .fileinput-new .input-group .btn-file.btn-sm { border-radius: 0 2px 2px 0; } .fileinput-new.input-group .btn-file.btn-lg, .fileinput-new .input-group .btn-file.btn-lg { border-radius: 0 2px 2px 0; } .form-group.has-warning .fileinput .fileinput-preview { color: #ffa829; } .form-group.has-warning .fileinput .thumbnail { border-color: #ff760f; } .form-group.has-error .fileinput .fileinput-preview { color: #f6675d; } .form-group.has-error .fileinput .thumbnail { border-color: #f54556; } .form-group.has-success .fileinput .fileinput-preview { color: #67bd6a; } .form-group.has-success .fileinput .thumbnail { border-color: #61b555; } .input-group-addon:not(:first-child) { border-left: 0; } /*! * Waves v0.7.4 * http://fian.my.id/Waves * * Copyright 2014 Alfiana E. Sibuea and other contributors * Released under the MIT license * https://github.com/fians/Waves/blob/master/LICENSE */ .waves-effect { position: relative; cursor: pointer; display: inline-block; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; } .waves-effect .waves-ripple { position: absolute; border-radius: 50%; width: 100px; height: 100px; margin-top: -50px; margin-left: -50px; opacity: 0; background: rgba(0, 0, 0, 0.2); background: -webkit-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; -webkit-transition-property: -webkit-transform, opacity; -moz-transition-property: -moz-transform, opacity; -o-transition-property: -o-transform, opacity; transition-property: transform, opacity; -webkit-transform: scale(0) translate(0, 0); -moz-transform: scale(0) translate(0, 0); -ms-transform: scale(0) translate(0, 0); -o-transform: scale(0) translate(0, 0); transform: scale(0) translate(0, 0); pointer-events: none; } .waves-effect.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); background: -webkit-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); } .waves-effect.waves-classic .waves-ripple { background: rgba(0, 0, 0, 0.2); } .waves-effect.waves-classic.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); } .waves-notransition { -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important; } .waves-button, .waves-circle { -webkit-transform: translateZ(0); -moz-transform: translateZ(0); -ms-transform: translateZ(0); -o-transform: translateZ(0); transform: translateZ(0); -webkit-mask-image: -webkit-radial-gradient(circle, #ffffff 100%, #000000 100%); } .waves-button, .waves-button:hover, .waves-button:visited, .waves-button-input { white-space: nowrap; vertical-align: middle; cursor: pointer; border: none; outline: none; color: inherit; background-color: rgba(0, 0, 0, 0); font-size: 1em; line-height: 1em; text-align: center; text-decoration: none; z-index: 1; } .waves-button { padding: 0.85em 1.1em; border-radius: 0.2em; } .waves-button-input { margin: 0; padding: 0.85em 1.1em; } .waves-input-wrapper { border-radius: 0.2em; vertical-align: bottom; } .waves-input-wrapper.waves-button { padding: 0; } .waves-input-wrapper .waves-button-input { position: relative; top: 0; left: 0; z-index: 1; } .waves-circle { text-align: center; width: 2.5em; height: 2.5em; line-height: 2.5em; border-radius: 50%; } .waves-float { -webkit-mask-image: none; -webkit-box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); -webkit-transition: all 300ms; -moz-transition: all 300ms; -o-transition: all 300ms; transition: all 300ms; } .waves-float:active { -webkit-box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); } .waves-block { display: block; } /* Firefox Bug: link not triggered */ a.waves-effect .waves-ripple { z-index: -1; } /* * Load Website related LESS files */ /* * Generate Margin Class * margin, margin-top, margin-bottom, margin-left, margin-right */ .m-0 { margin: 0px !important; } .m-t-0 { margin-top: 0px !important; } .m-b-0 { margin-bottom: 0px !important; } .m-l-0 { margin-left: 0px !important; } .m-r-0 { margin-right: 0px !important; } .m-5 { margin: 5px !important; } .m-t-5 { margin-top: 5px !important; } .m-b-5 { margin-bottom: 5px !important; } .m-l-5 { margin-left: 5px !important; } .m-r-5 { margin-right: 5px !important; } .m-10 { margin: 10px !important; } .m-t-10 { margin-top: 10px !important; } .m-b-10 { margin-bottom: 10px !important; } .m-l-10 { margin-left: 10px !important; } .m-r-10 { margin-right: 10px !important; } .m-15 { margin: 15px !important; } .m-t-15 { margin-top: 15px !important; } .m-b-15 { margin-bottom: 15px !important; } .m-l-15 { margin-left: 15px !important; } .m-r-15 { margin-right: 15px !important; } .m-20 { margin: 20px !important; } .m-t-20 { margin-top: 20px !important; } .m-b-20 { margin-bottom: 20px !important; } .m-l-20 { margin-left: 20px !important; } .m-r-20 { margin-right: 20px !important; } .m-25 { margin: 25px !important; } .m-t-25 { margin-top: 25px !important; } .m-b-25 { margin-bottom: 25px !important; } .m-l-25 { margin-left: 25px !important; } .m-r-25 { margin-right: 25px !important; } .m-30 { margin: 30px !important; } .m-t-30 { margin-top: 30px !important; } .m-b-30 { margin-bottom: 30px !important; } .m-l-30 { margin-left: 30px !important; } .m-r-30 { margin-right: 30px !important; } /* * Generate Padding Class * padding, padding-top, padding-bottom, padding-left, padding-right */ .p-0 { padding: 0px !important; } .p-t-0 { padding-top: 0px !important; } .p-b-0 { padding-bottom: 0px !important; } .p-l-0 { padding-left: 0px !important; } .p-r-0 { padding-right: 0px !important; } .p-5 { padding: 5px !important; } .p-t-5 { padding-top: 5px !important; } .p-b-5 { padding-bottom: 5px !important; } .p-l-5 { padding-left: 5px !important; } .p-r-5 { padding-right: 5px !important; } .p-10 { padding: 10px !important; } .p-t-10 { padding-top: 10px !important; } .p-b-10 { padding-bottom: 10px !important; } .p-l-10 { padding-left: 10px !important; } .p-r-10 { padding-right: 10px !important; } .p-15 { padding: 15px !important; } .p-t-15 { padding-top: 15px !important; } .p-b-15 { padding-bottom: 15px !important; } .p-l-15 { padding-left: 15px !important; } .p-r-15 { padding-right: 15px !important; } .p-20 { padding: 20px !important; } .p-t-20 { padding-top: 20px !important; } .p-b-20 { padding-bottom: 20px !important; } .p-l-20 { padding-left: 20px !important; } .p-r-20 { padding-right: 20px !important; } .p-25 { padding: 25px !important; } .p-t-25 { padding-top: 25px !important; } .p-b-25 { padding-bottom: 25px !important; } .p-l-25 { padding-left: 25px !important; } .p-r-25 { padding-right: 25px !important; } .p-30 { padding: 30px !important; } .p-t-30 { padding-top: 30px !important; } .p-b-30 { padding-bottom: 30px !important; } .p-l-30 { padding-left: 30px !important; } .p-r-30 { padding-right: 30px !important; } /* * Generate Font-Size Classes (8px - 20px) */ .f-8 { font-size: 8px !important; } .f-9 { font-size: 9px !important; } .f-10 { font-size: 10px !important; } .f-11 { font-size: 11px !important; } .f-12 { font-size: 12px !important; } .f-13 { font-size: 13px !important; } .f-14 { font-size: 14px !important; } .f-15 { font-size: 15px !important; } .f-16 { font-size: 16px !important; } .f-17 { font-size: 17px !important; } .f-18 { font-size: 18px !important; } .f-19 { font-size: 19px !important; } .f-20 { font-size: 20px !important; } /* * Font Weight */ .f-300 { font-weight: 300 !important; } .f-400 { font-weight: 400 !important; } .f-500 { font-weight: 500 !important; } .f-700 { font-weight: 700 !important; } /* * Position Classes */ .p-relative { position: relative !important; } .p-absolute { position: absolute !important; } .p-fixed { position: fixed !important; } .p-static { position: static !important; } /* * Overflow */ .o-hidden { overflow: hidden !important; } .o-visible { overflow: visible !important; } .o-auto { overflow: auto !important; } /* * Display */ .d-block { display: block !important; } .di-block { display: inline-block !important; } /* * Material Background Colors */ .bgm-white { background-color: #ffffff !important; } .c-white { color: #ffffff !important; } .bgm-black { background-color: #000000 !important; } .c-black { color: #000000 !important; } .bgm-brown { background-color: #795548 !important; } .c-brown { color: #795548 !important; } .bgm-pink { background-color: #e91e63 !important; } .c-pink { color: #e91e63 !important; } .bgm-red { background-color: #f44336 !important; } .c-red { color: #f44336 !important; } .bgm-blue { background-color: #2196f3 !important; } .c-blue { color: #2196f3 !important; } .bgm-purple { background-color: #9c27b0 !important; } .c-purple { color: #9c27b0 !important; } .bgm-deeppurple { background-color: #673ab7 !important; } .c-deeppurple { color: #673ab7 !important; } .bgm-lightblue { background-color: #03a9f4 !important; } .c-lightblue { color: #03a9f4 !important; } .bgm-cyan { background-color: #00bcd4 !important; } .c-cyan { color: #00bcd4 !important; } .bgm-teal { background-color: #009688 !important; } .c-teal { color: #009688 !important; } .bgm-green { background-color: #4caf50 !important; } .c-green { color: #4caf50 !important; } .bgm-lightgreen { background-color: #8bc34a !important; } .c-lightgreen { color: #8bc34a !important; } .bgm-lime { background-color: #cddc39 !important; } .c-lime { color: #cddc39 !important; } .bgm-yellow { background-color: #ffeb3b !important; } .c-yellow { color: #ffeb3b !important; } .bgm-amber { background-color: #ffc107 !important; } .c-amber { color: #ffc107 !important; } .bgm-orange { background-color: #ff9800 !important; } .c-orange { color: #ff9800 !important; } .bgm-deeporange { background-color: #ff5722 !important; } .c-deeporange { color: #ff5722 !important; } .bgm-gray { background-color: #9e9e9e !important; } .c-gray { color: #9e9e9e !important; } .bgm-bluegray { background-color: #607d8b !important; } .c-bluegray { color: #607d8b !important; } .bgm-indigo { background-color: #3f51b5 !important; } .c-indigo { color: #3f51b5 !important; } /* * Background Colors */ .bg-black-trp { background-color: rgba(0, 0, 0, 0.1) !important; } /* * Border */ .b-0 { border: 0 !important; } /* * width */ .w-100 { width: 100% !important; } /* * Border Radius */ .brd-2 { border-radius: 2px; } /* * Media - Overriding the Media object to 3.2 version in order to prevent issues like text overflow. */ .media { overflow: visible; } .media:before, .media:after { content: " "; display: table; } .media:after { clear: both; } .media:before, .media:after { content: " "; display: table; } .media:after { clear: both; } .media > .pull-left { padding-right: 15px; } .media > .pull-right { padding-left: 15px; } .media-heading { font-size: 14px; margin-bottom: 10px; } .media-body { zoom: 1; display: block; width: auto; } .media-object { border-radius: 2px; } .close { opacity: 0.5; filter: alpha(opacity=50); font-weight: normal; text-shadow: none; } .close:hover { color: inherit; opacity: 1; filter: alpha(opacity=100); } .dl-horizontal dt { text-align: left; } *, button, input, i, a { -webkit-font-smoothing: antialiased; } *, *:active, *:hover { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } html { overflow-x: hidden\0/; -ms-overflow-style: none; } html, body { min-height: 100vh; } body { font-weight: 400; position: relative; } audio, video { outline: none; } p { margin-bottom: 20px; } small { font-size: 11px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small { font-size: 12px; } #main { position: relative; padding-bottom: 110px; padding-top: 100px; } .container.c-alt { max-width: 1170px; } @media (min-width: 768px) and (max-width: 1199px) { #content { padding-left: 15px; padding-right: 15px; } } @media (min-width: 1200px) { body.sw-toggled #content { padding-left: 268px; } } @media (min-width: 1200px) { body.sw-toggled #content > .container { width: calc(100% - 30px); } } .clist { list-style: none; } .clist > li:before { font-family: 'Material-Design-Iconic-Font'; margin: 0 10px 0 -20px; vertical-align: middle; } .clist.clist-angle > li:before { content: "\f2fb"; } .clist.clist-check > li:before { content: "\f26b"; } .clist.clist-star > li:before { content: "\f27d"; } /* * Common header classes & IDs * Do not remove this */ .header-inner { list-style: none; padding: 17px 0; margin-bottom: 0; position: relative; } .header-inner > li:not(.pull-right) { float: left; } .header-inner > li:not(:last-child) { margin-right: -2px; } .logo a { color: #fff; text-transform: uppercase; display: block; font-size: 16px; } #menu-trigger { width: 65px; height: 35px; cursor: pointer; } #menu-trigger .line-wrap .line { background-color: #fff; } #menu-trigger:before { content: ""; position: absolute; top: 13px; left: 7px; width: 45px; height: 45px; border-radius: 50%; background: rgba(255, 255, 255, 0.22); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); z-index: 0; } #menu-trigger.open:before { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .top-menu { list-style: none; padding: 0; } .top-menu > li { display: inline-block; margin: 0 1px; vertical-align: top; min-width: 50px; } @media (max-width: 767px) { .top-menu > li { position: static !important; } } .top-menu > li .dropdown-menu-lg { padding: 0; } .top-menu > li .dropdown-menu-lg .lv-body { min-height: 295px; overflow-x: hidden; } @media (min-width: 768px) { .top-menu > li:not(#toggle-width) { position: relative; } .top-menu > li:not(#toggle-width):before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.12); z-index: 0; border-radius: 2px; opacity: 0; filter: alpha(opacity=0); } .top-menu > li:not(#toggle-width):hover:before, .top-menu > li:not(#toggle-width).open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .top-menu > li > a { color: #fff; display: block; text-align: center; z-index: 1; position: relative; } .top-menu > li > a > .tm-icon { font-size: 24px; line-height: 36px; } .top-menu > li > a > .tm-label { line-height: 35px; white-space: nowrap; padding: 0 10px; font-size: 14px; text-transform: uppercase; } .top-menu > li > a > .tmn-counts { position: absolute; font-style: normal; background: #f44336; padding: 1px 5px; border-radius: 2px; right: 7px; top: -3px; font-size: 10px; line-height: 15px; } @media (max-width: 767px) { .top-menu .dropdown-menu-lg { width: calc(100% - 28px) !important; } .top-menu .dropdown-menu { right: 14px; top: 55px; } } #notifications { position: relative; } #notifications .lv-body { overflow-x: hidden; } #notifications:before { content: ""; position: absolute; width: 100%; height: calc(100% - 70px); background: url(../img/notifications.png) no-repeat center; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 400ms; transition-duration: 400ms; -webkit-transform: scale(0) rotate(-180deg); -ms-transform: scale(0) rotate(-180deg); -o-transform: scale(0) rotate(-180deg); transform: scale(0) rotate(-180deg); opacity: 0; filter: alpha(opacity=0); top: 42px; } #notifications.empty:before { -webkit-transform: scale(1) rotate(0deg); -ms-transform: scale(1) rotate(0deg); -o-transform: scale(1) rotate(0deg); transform: scale(1) rotate(0deg); opacity: 1; filter: alpha(opacity=100); } /* Full Screen */ :-webkit-full-screen [data-action="fullscreen"] { display: none; } :-moz-full-screen [data-action="fullscreen"] { display: none; } :-ms-fullscreen [data-action="fullscreen"] { display: none; } :full-screen [data-action="fullscreen"] { display: none; } :fullscreen [data-action="fullscreen"] { display: none; } /* ----------------------------- End common header classes and IDs------------------------------------- */ /* * For header type 1 only * You may remove these if you opt header 2 */ #header { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); min-height: 70px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; position: fixed; z-index: 11; width: 100%; left: 0; top: 0; padding: 0 11px; } #header:not(.sidebar-toggled).header-up { -webkit-transform: translate3d(0, -70px, 0); transform: translate3d(0, -70px, 0); } #header .logo a { padding: 7px 10px; } #top-search-wrap { position: absolute; top: -65px; left: 0; width: 100%; height: 70px; background: #fff; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; opacity: 0; filter: alpha(opacity=0); z-index: 10; } #top-search-wrap input[type="text"] { border: 0; height: 40px; padding: 0 10px 0 55px; font-size: 18px; width: 500px; border-radius: 2px; background-color: #efefef; width: 100%; } #top-search-wrap #top-search-close { position: absolute; top: 15px; font-size: 23px; font-style: normal; width: 45px; text-align: center; border-radius: 2px 0px 0px 2px; cursor: pointer; left: 15px; height: 40px; padding-top: 9px; } #top-search-wrap #top-search-close:hover { background-color: #e3e3e3; } @media (max-width: 767px) { #top-search-wrap #top-search-close { right: 7px; } } .tsw-inner { position: relative; padding: 15px; max-width: 700px; display: block; margin: 0 auto; } .search-toggled #top-search-wrap { top: 0; opacity: 1; filter: alpha(opacity=100); } /* Full Width Layout */ @media (min-width: 1200px) { #toggle-width .toggle-switch { margin: 9px 30px 0 0; } #toggle-width .toggle-switch .ts-helper { height: 11px; width: 33px; } #toggle-width .toggle-switch .ts-helper:before { width: 20px; height: 20px; top: -5px; } #toggle-width .toggle-switch input:checked + .ts-helper { background: rgba(0, 0, 0, 0.26); } #toggle-width .toggle-switch input:checked + .ts-helper:before { left: 18px; background: #fff; } } @media (max-width: 1200px) { #toggle-width { display: none; } } @media (min-width: 1200px) { .sw-toggled #header { padding-left: 15px; } .sw-toggled #menu-trigger { display: none; } } /* For Stupid IE9 */ .ie9 #header:not(.sidebar-toggled).header-up { display: none; } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For Header type 2 only * You may remove these if you opt header 1 */ #header-2 { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); position: relative; margin-bottom: -70px; z-index: 10; } @media (min-width: 768px) { #header-2 { padding: 15px 30px 0; } #header-2:before { content: ""; position: absolute; bottom: 0; left: 0; background: rgba(0, 0, 0, 0.04); width: 100%; height: 49px; } } #header-2 .search { margin-bottom: 25px; } @media (max-width: 767px) { #header-2 .search { padding: 0 20px; } } #header-2 .search input[type="text"] { width: 100%; background: transparent; border: 0; border-bottom: 1px solid rgba(255, 255, 255, 0.24); color: #fff; font-size: 15px; font-weight: 300; padding: 6px 0 6px 30px; } #header-2 .search input[type="text"]::-moz-placeholder { color: #ffffff; opacity: 1; } #header-2 .search input[type="text"]:-ms-input-placeholder { color: #ffffff; } #header-2 .search input[type="text"]::-webkit-input-placeholder { color: #ffffff; } #header-2 .search:after { background: #ffeb3b; } #header-2 .search .fg-line { max-width: 500px; position: relative; } #header-2 .search .fg-line:after { background: #ffeb3b; } #header-2 .search .fg-line:before { content: '\f1c3'; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 0; bottom: 1px; color: #fff; font-size: 22px; } .ha-menu > ul { list-style: none; padding: 0; margin: 0; } .ha-menu > ul > li { display: inline-block; vertical-align: top; } @media (max-width: 767px) { .ha-menu > ul > li { display: block; } } .ha-menu > ul > li:not(.active) > *:not(ul) { color: rgba(255, 255, 255, 0.6); } .ha-menu > ul > li.active > *:not(ul) { color: #fff; box-shadow: inset 0px -3px 0 0px #ffeb3b; } @media (max-width: 767px) { .ha-menu > ul > li.active > *:not(ul) { display: block; } } .ha-menu > ul > li > *:not(ul) { text-transform: uppercase; padding: 15px 12px; display: block; } .ha-menu > ul > li.open > *:not(ul), .ha-menu > ul > li > *:not(ul):hover { color: #fff; } .ha-menu > ul > li .dropdown-menu { margin-top: -5px; min-width: 100%; } @media (max-width: 767px) { .ha-menu { width: 200px; position: absolute; top: 65px; left: 8px; box-shadow: 0 0 10px; z-index: 10; padding: 0 10px; } .ha-menu:not(.toggled) { display: none; } } .sidebar { position: fixed; background: #fff; box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); height: calc(100% - 65px); top: 65px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; z-index: 10; opacity: 0; filter: alpha(opacity=0); overflow-y: auto; } .sidebar.toggled { opacity: 1; filter: alpha(opacity=100); } #sidebar { width: 268px; -webkit-transform: translate3d(-268px, 0, 0); transform: translate3d(-268px, 0, 0); } #sidebar.toggled { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .profile-menu > a { display: block; height: 129px; margin-bottom: 5px; width: 100%; background: url(../img/profile-menu.png) no-repeat left top; background-size: 100%; } .profile-menu > a .profile-pic { padding: 12px; } .profile-menu > a .profile-pic > img { width: 47px; height: 47px; border-radius: 50%; border: 3px solid rgba(0, 0, 0, 0.14); box-sizing: content-box; } .profile-menu > a .profile-info { background: rgba(0, 0, 0, 0.37); padding: 7px 14px; color: #fff; margin-top: 20px; position: relative; } .profile-menu > a .profile-info > i { font-size: 19px; line-height: 100%; position: absolute; right: 15px; top: 7px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-menu .main-menu { display: none; margin: 0 0 0; border-bottom: 1px solid #E6E6E6; } .profile-menu.toggled .profile-info > i { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); } .main-menu { list-style: none; padding-left: 0; margin: 20px 0 0 0; } .main-menu > li > a { padding: 14px 20px 14px 52px; display: block; color: #4C4C4C; font-weight: 500; position: relative; } .main-menu > li > a:hover { color: #262626; background-color: #f7f7f7; } .main-menu > li > a > i { position: absolute; left: 16px; font-size: 20px; top: 0; width: 25px; text-align: center; padding: 13px 0; } .main-menu > li.active > a { color: #262626; background-color: #F4F4F4; } .sub-menu > a { position: relative; } .sub-menu > a:before, .sub-menu > a:after { position: absolute; top: 12px; color: #575757; font-family: 'Material-Design-Iconic-Font'; font-size: 17px; right: 15px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .sub-menu > a:before { content: "\f278"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .sub-menu > a:after { content: "\f273"; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .sub-menu .sub-menu > a:before, .sub-menu .sub-menu > a:after { top: 5px; } .sub-menu.toggled > a:before { content: "\f278"; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .sub-menu.toggled > a:after { content: "\f273"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .sub-menu ul { list-style: none; display: none; padding: 0; } .sub-menu ul > li > a { color: #7f7f7f; padding: 8px 20px 8px 50px; font-weight: 500; display: block; } .sub-menu ul > li > a.active, .sub-menu ul > li > a:hover { color: #000; } .sub-menu ul > li:first-child > a { padding-top: 14px; } .sub-menu ul > li:last-child > a { padding-bottom: 16px; } .sub-menu ul > li ul { font-size: 12px; margin: 10px 0; background-color: #f7f7f7; } .sub-menu.active > ul { display: block; } /* * layout */ body:not(.sw-toggled) #sidebar { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } @media (min-width: 1200px) { body.sw-toggled #sidebar { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; filter: alpha(opacity=100); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } @media (max-width: 1199px) { body.sw-toggled #sidebar { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } } /* * For Stupid IE9 */ @media (min-width: 1200px) { .ie9 body.sw-toggled #sidebar { display: block; } } .ie9 body:not(.sw-toggled) #sidebar:not(.toggled) { display: none; } .dropdown-menu { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .dropdown-menu > li > a { padding: 8px 17px; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .dropdown-menu.dropdown-menu-lg { width: 300px; } .dropdown-menu.dropdown-menu-sm { width: 150px; } .dropdown-menu.dropdown-menu-right { right: 0; left: auto; } .dropdown-menu.dropdown-menu-right > li > a { text-align: right; } .dropdown-menu.dm-icon > li > a > .zmdi { line-height: 100%; vertical-align: top; font-size: 18px; width: 28px; } .dropdown-menu:not([class*="bgm-"]) > li > a { color: #4C4C4C; } .dropdown-menu:not([class*="bgm-"]) > li > a:hover { color: #000; } .dropdown-menu[class*="bgm-"] > li > a { font-weight: 300; color: #fff; } .dropdown:not([data-animation]) .dropdown-menu, .btn-group:not([data-animation]) .dropdown-menu { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); display: block; } .dropdown .dropdown-menu:not([data-animation]).pull-right, .bootstrap-select .dropdown-menu:not([data-animation]).pull-right, .btn-group .dropdown-menu:not([data-animation]).pull-right, .dropdown .dropdown-menu:not([data-animation]).dropdown-menu-right, .bootstrap-select .dropdown-menu:not([data-animation]).dropdown-menu-right, .btn-group .dropdown-menu:not([data-animation]).dropdown-menu-right { -webkit-transform-origin: top right; -moz-transform-origin: top right; -ms-transform-origin: top right; transform-origin: top right; } .dropdown .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right), .bootstrap-select .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right), .btn-group .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right) { -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; } .dropup .dropdown-menu:not([data-animation]).pull-right, .dropup .dropdown-menu:not([data-animation]).dropdown-menu-right { -webkit-transform-origin: bottom right; -moz-transform-origin: bottom right; -ms-transform-origin: bottom right; transform-origin: bottom right; } .dropup .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right) { -webkit-transform-origin: bottom left; -moz-transform-origin: bottom left; -ms-transform-origin: bottom left; transform-origin: bottom left; } .dropdown.open .dropdown-menu:not([data-animation]), .dropup.open .dropdown-menu:not([data-animation]), .bootstrap-select.open .dropdown-menu:not([data-animation]), .btn-group.open .dropdown-menu:not([data-animation]) { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .dropdown-header { padding: 3px 17px; margin-top: 10px; color: #b1b1b1; text-transform: uppercase; font-weight: normal; } .btn-group.open .dropdown-toggle { box-shadow: none; } .listview { position: relative; } .listview:not(.lv-lg):not(.lv-message) .lv-item { padding: 10px 20px; } @media (min-width: 480px) { .listview.lv-lg .lv-item { padding: 17px 35px 17px 25px; } } @media (max-width: 767px) { .listview.lv-lg .lv-item { padding: 17px 35px 17px 20px; } } .listview.lv-lg .lv-item:hover { background-color: #FFFFDB; } .listview .lv-item { position: relative; display: block; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .listview .lv-item .lv-small { font-size: 12px; color: #A9A9A9; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; width: 100%; } .listview .lv-item .checkbox, .listview .lv-item.media { margin: 0; } .listview .lv-item .lv-actions { position: absolute; right: 15px; top: 10px; } @media (max-width: 480px) { .listview .lv-item .lv-actions { right: 7px; } } .listview .lv-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; } .listview a.lv-item:hover { background: #ECF9FF; } .listview [class*="lv-img"] { border-radius: 50%; } .listview .lv-img { width: 48px; height: 48px; } .listview .lv-img-sm { width: 35px; height: 35px; } .listview.lv-bordered .lv-item:not(:last-child) { border-bottom: 1px solid #f0f0f0; } .listview .lv-attrs { list-style: none; padding: 0; margin: 5px 0 0 0; } .listview .lv-attrs > li { display: inline-block; padding: 2px 10px 3px; font-size: 12px; margin-top: 5px; margin-right: 2px; } .listview .lv-attrs > li:not(.info):not(.primary):not(.warning):not(.danger) { border: 1px solid #dedede; background: #ffffff; color: #5e5e5e; } .listview .lv-attrs > li.info { border: 1px solid #00bcd4; background: #00bcd4; color: #ffffff; } .listview .lv-attrs > li.primary { border: 1px solid #2196f3; background: #2196f3; color: #ffffff; } .listview .lv-attrs > li.warning { border: 1px solid #ff9800; background: #ff9800; color: #ffffff; } .listview .lv-attrs > li.danger { border: 1px solid #f44336; background: #f44336; color: #ffffff; } .listview .lv-attrs > li > a { display: block; } .listview:not(.lv-message) .lv-title { color: #000; } [class*="lv-img"] { border-radius: 50%; } .lv-img { width: 48px; height: 48px; } .lv-img-sm { width: 35px; height: 35px; } .lv-header { text-align: center; padding: 15px 10px 13px; line-height: 100%; text-transform: uppercase; border-bottom: 1px solid #F0F0F0; font-weight: 500; color: #4C4C4C; margin-bottom: 10px; } .lv-header .actions { position: absolute; top: 6px; right: 8px; z-index: 10; } .lvh-search { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 4; background: #fff; display: none; } .lvh-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 24px; top: 17px; font-size: 22px; } .lvhs-input { border: 0; padding: 0 26px 0 55px; height: 63px; font-size: 18px; width: 100%; font-weight: 100; background: #fff; border-bottom: 1px solid #EEE; } .lvh-search-close { font-style: normal; position: absolute; top: 23px; right: 22px; font-size: 17px; width: 18px; height: 18px; background-color: #ADADAD; line-height: 100%; color: #fff; text-align: center; cursor: pointer; border-radius: 50%; } .lvh-search-close:hover { background: #333; } .lv-header-alt { position: relative; background: #f8f8f8; padding: 15px; } .lv-header-alt .lv-actions { z-index: 3; float: right; margin-top: 3px; position: relative; } .lv-header-alt .lv-actions > li > a { margin: 0 3px; } .lvh-label { color: #818181; display: inline-block; margin: 0; font-size: 14px; font-weight: normal; padding: 0 6px; line-height: 33px; vertical-align: middle; float: left; } .lv-footer { display: block; text-align: center; padding: 7px 10px 8px; border-top: 1px solid #F0F0F0; line-height: 100%; font-size: 11px; margin-top: 20px; color: #828282; } a.lv-footer:hover { color: #050505; } /* * Inside Card will have more padding */ .card-body .lv-item { padding: 12px 20px; } .progress { box-shadow: none; border-radius: 0; height: 5px; margin-bottom: 0; } .progress .progress-bar { box-shadow: none; } #chat { padding: 20px 0; width: 280px; right: -300px; } #chat.toggled { right: 0; } #chat .chat-search { padding: 20px 20px 15px 20px; } #chat .chat-search .form-control { background-image: url("../img/icons/search-2.png"); background-repeat: no-repeat; background-position: left center; padding-left: 30px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #chat .chat-search .form-control { background-image: url("../img/icons/search-2@2x.png"); background-size: 24px 24px; } } #chat .chat-search .form-control:focus { background-position: right center; padding: 0 30px 0 0; } /* * Chat Status Icons */ [class*="chat-status"] { position: absolute; width: 10px; height: 10px; border-radius: 50%; top: -3px; right: 12px; border: 2px solid #FFF; } /* Simple Mixin */ .chat-status-online { box-shadow: 0 0 0 1px #1ec01e; background: #1ec01e; } .chat-status-offline { box-shadow: 0 0 0 1px #e73f3f; background: #e73f3f; } .chat-status-busy { box-shadow: 0 0 0 1px #ffa500; background: #ffa500; } /* * For Stupid IE9 */ .ie9 #chat { right: 0; } .ie9 #chat:not(.toggled) { display: none; } .tab-nav { list-style: none; padding: 0; white-space: nowrap; margin: 0; overflow: auto; box-shadow: inset 0 -2px 0 0 #eeeeee; width: 100%; } .tab-nav li { display: inline-block; vertical-align: top; } .tab-nav li > a { display: inline-block; color: #7a7a7a; text-transform: uppercase; position: relative; width: 100%; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; font-weight: 500; } .tab-nav li > a:after { content: ""; height: 2px; position: absolute; width: 100%; left: 0; bottom: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } @media (min-width: 768px) { .tab-nav li > a { padding: 15px; } } @media (max-width: 768px) { .tab-nav li > a { padding: 15px 8px; } } .tab-nav li.active > a { color: #000; } .tab-nav li.active > a:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .tab-nav.tab-nav-right { text-align: right; } .tab-nav.tn-justified > li { display: table-cell; width: 1%; text-align: center; } .tab-nav.tn-icon > li .zmdi { font-size: 22px; line-height: 100%; min-height: 25px; } .tab-nav:not([data-tab-color]) > li > a:after { background: #2196f3; } .tab-nav[data-tab-color="green"] > li > a:after { background: #4caf50; } .tab-nav[data-tab-color="red"] > li > a:after { background: #f44336; } .tab-nav[data-tab-color="teal"] > li > a:after { background: #009688; } .tab-nav[data-tab-color="amber"] > li > a:after { background: #ffc107; } .tab-nav[data-tab-color="black"] > li > a:after { background: #000000; } .tab-nav[data-tab-color="cyan"] > li > a:after { background: #00bcd4; } .tab-content { padding: 20px 0; } .card { position: relative; background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); margin-bottom: 30px; } .card .card-header { position: relative; } @media screen and (min-width: 768px) { .card .card-header:not(.ch-alt) { padding: 23px 25px; } } @media screen and (max-width: 991px) { .card .card-header:not(.ch-alt) { padding: 18px; } } .card .card-header h2 { margin: 0; line-height: 100%; font-size: 17px; font-weight: 400; } .card .card-header h2 small { display: block; margin-top: 8px; color: #AEAEAE; line-height: 160%; } @media screen and (min-width: 768px) { .card .card-header.ch-alt { padding: 23px 26px; } } @media screen and (max-width: 991px) { .card .card-header.ch-alt { padding: 18px 18px 28px; } } .card .card-header.ch-alt:not([class*="bgm-"]) { background-color: #f7f7f7; } .card .card-header[class*="bgm-"] h2, .card .card-header[class*="bgm-"] h2 small { color: #fff; } .card .card-header .actions { position: absolute; right: 10px; z-index: 2; top: 15px; } .card .card-header .btn-float { right: 25px; bottom: -23px; z-index: 1; } @media screen and (min-width: 768px) { .card .card-body.card-padding { padding: 23px 26px; } } @media screen and (max-width: 991px) { .card .card-body.card-padding { padding: 18px; } } .card .card-body.card-padding-sm { padding: 15px; } .card-header:not(.ch-alt):not([class*="bgm-"]) + .card-padding { padding-top: 0; } .chart-edge { margin: 20px -8px 0 -10px; overflow: hidden; } .chart-edge .flot-chart { bottom: -14px; } .charts-row { margin-top: 50px; margin-bottom: 20px; } .mini-charts-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; } .mini-charts-item .chart { padding: 15px; float: left; } .mini-charts-item .chart.chart-pie { margin: 0 20px; } .mini-charts-item .count { overflow: hidden; color: rgba(255, 255, 255, 0.9); padding: 16px 12px; } .mini-charts-item .count > h2 { margin: 0; line-height: 100%; font-size: 22px; font-weight: 300; color: #fff; } .mini-charts-item .count > small { margin-bottom: 2px; display: block; } .mini-charts-item .count > h2, .mini-charts-item .count > small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .mini-charts-item > .clearfix, .mini-charts-item > .dl-horizontal dd, .mini-charts-item > .container, .mini-charts-item > .container-fluid, .mini-charts-item > .row, .mini-charts-item > .form-horizontal .form-group, .mini-charts-item > .btn-toolbar, .mini-charts-item > .btn-group-vertical > .btn-group, .mini-charts-item > .nav, .mini-charts-item > .navbar, .mini-charts-item > .navbar-header, .mini-charts-item > .navbar-collapse, .mini-charts-item > .pager, .mini-charts-item > .panel-body, .mini-charts-item > .modal-header, .mini-charts-item > .modal-footer { position: relative; z-index: 1; } .mini-charts-item:before { -webkit-transition: width; -o-transition: width; transition: width; -webkit-transition-duration: 500ms; transition-duration: 500ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; content: ""; width: 113px; height: 100%; background: rgba(0, 0, 0, 0.1); position: absolute; left: 0; top: 0; } .mini-charts-item:hover .count { color: #fff !important; } .mini-charts-item:hover:before { width: 100%; } /* * Sparkline Tooltip */ #jqstooltip { min-width: 21px; min-height: 23px; text-align: center; border: 0; background: #fff; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); background-color: #fff; } #jqstooltip .jqsfield { font-size: 12px; font-weight: 700; font-family: inherit; text-align: center; color: #333; } #jqstooltip .jqsfield > span { display: none; } /* * Easy Pie Charts */ .epc-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; padding: 30px 20px; text-align: center; } .easy-pie { display: inline-block; position: relative; padding: 0 5px 10px; } .easy-pie .percent { position: absolute; font-weight: 300; width: 100%; line-height: 100%; left: 0; } .easy-pie .percent:after { content: "%"; } .easy-pie.main-pie .percent { margin-top: 49px; font-size: 50px; text-align: center; } .easy-pie.main-pie .percent:not([class*="c-"]) { color: rgba(255, 255, 255, 0.7); } .easy-pie.main-pie .percent:after { font-size: 30px; } .easy-pie.main-pie .pie-title { color: #fff; } .easy-pie:not(.main-pie) .percent { font-size: 26px; margin-top: 37px; } .easy-pie:not(.main-pie) .percent:after { font-size: 20px; } .easy-pie .pie-title { position: absolute; width: 100%; text-align: center; bottom: -3px; left: 0; } /* * Recet Items Table Chart */ #recent-items-chart { width: calc(100% + 19px); height: 150px; margin: -20px -10px 0; bottom: -10px; } /* * Flot Chart */ [class*="flot-chart"] { width: 100%; display: block; } .flot-chart { height: 200px; } .flot-chart-pie { height: 300px; } @media (min-width: 768px) { .flot-chart-pie { margin-bottom: 20px; } } .flot-tooltip, #flotTip { position: absolute; color: #333; display: none; font-size: 12px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); padding: 3px 10px; background-color: #fff; z-index: 99999; } [class*="flc-"] { text-align: center; margin: 10px 0 5px; } [class*="flc-"] table { display: inline-block; } [class*="flc-"] .legendColorBox > div { border: #fff !important; } [class*="flc-"] .legendColorBox > div > div { border-radius: 50%; } [class*="flc-"] .legendLabel { padding: 0 8px 0 3px; } .dash-widget-item { position: relative; min-height: 380px; margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } .dash-widget-item .dash-widget-header { position: relative; } .dash-widget-item .dash-widget-header .actions { display: none; position: absolute; right: 4px; top: 6px; } .dash-widget-item .dash-widget-footer { position: absolute; left: 0; bottom: 0; width: 100%; } .dash-widget-item .dash-widget-title { padding: 12px 20px; position: absolute; width: 100%; left: 0; font-weight: 300; } .dash-widget-item:hover .dash-widget-header .actions { display: block; } /* * Site Visits */ #site-visits { color: rgba(255, 255, 255, 0.9); } #site-visits .dash-widget-header { padding-bottom: 38px; background-color: rgba(0, 0, 0, 0.13); } #site-visits .dash-widget-title { bottom: 0; background: rgba(0, 0, 0, 0.15); color: rgba(255, 255, 255, 0.9); } #site-visits h3 { color: rgba(255, 255, 255, 0.9); } /* * Best Selling Item */ #best-selling { background-color: #fff; } #best-selling .dash-widget-header > img { width: 100%; height: 155px; } #best-selling .dash-widget-header .dash-widget-title { padding-bottom: 30px; top: 0; color: #fff; background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#99000000', endColorstr='#00000000', GradientType=0); } #best-selling .dash-widget-header .main-item { padding: 15px; color: #fff; position: absolute; bottom: 0; left: 0; width: 100%; background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#99000000', GradientType=0); } #best-selling .dash-widget-header .main-item > h2 { font-weight: 400; font-size: 20px; margin: 5px 0 0 0; line-height: 100%; color: #fff; } /* * Weather */ #weather-widget { color: #fff; padding: 20px 20px 0; } #weather-widget .weather-status { font-size: 40px; line-height: 100%; } #weather-widget .weather-icon { text-align: center; margin-top: 10px; height: 150px; background-repeat: no-repeat; background-position: center; /* Weather Icons */ } #weather-widget .weather-icon.wi-0 { background-image: url("../img/icons/weather/0.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-0 { background-image: url("../img/icons/weather/0@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-1 { background-image: url("../img/icons/weather/1.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-1 { background-image: url("../img/icons/weather/1@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-2 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-2 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-3 { background-image: url("../img/icons/weather/3.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-3 { background-image: url("../img/icons/weather/3@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-4 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-4 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-5 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-5 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-6 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-6 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-7 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-7 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-8 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-8 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-9 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-9 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-10 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-10 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-11 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-11 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-12 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-12 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-13 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-13 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-14 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-14 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-15 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-15 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-16 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-16 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-17 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-17 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-18 { background-image: url("../img/icons/weather/18.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-18 { background-image: url("../img/icons/weather/18@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-19 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-19 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-20 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-20 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-21 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-21 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-22 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-22 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-23 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-23 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-24 { background-image: url("../img/icons/weather/24.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-24 { background-image: url("../img/icons/weather/24@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-25 { background-image: url("../img/icons/weather/24.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-25 { background-image: url("../img/icons/weather/24@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-26 { background-image: url("../img/icons/weather/26.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-26 { background-image: url("../img/icons/weather/26@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-27 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-27 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-28 { background-image: url("../img/icons/weather/28.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-28 { background-image: url("../img/icons/weather/28@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-29 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-29 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-30 { background-image: url("../img/icons/weather/28.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-30 { background-image: url("../img/icons/weather/28@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-31 { background-image: url("../img/icons/weather/31.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-31 { background-image: url("../img/icons/weather/31@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-32 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-32 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-33 { background-image: url("../img/icons/weather/31.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-33 { background-image: url("../img/icons/weather/31@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-34 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-34 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-35 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-35 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-36 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-36 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-37 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-37 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-38 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-38 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-39 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-39 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-40 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-40 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-41 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-41 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-42 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-42 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-43 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-43 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-44 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-44 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-45 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-45 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-46 { background-image: url("../img/icons/weather/18.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-46 { background-image: url("../img/icons/weather/18@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-47 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-47 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-info { list-style: none; padding: 0; margin: 3px 0 0 0; } #weather-widget .weather-info > li { display: inline-block; border: 1px solid rgba(255, 255, 255, 0.39); padding: 2px 10px 3px; margin-right: 5px; } #weather-widget .weather-list { background: rgba(0, 0, 0, 0.08); padding: 5px 12px; font-size: 16px; height: 51px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #weather-widget .weather-list > span { margin-right: 7px; font-weight: 300; display: inline-block; line-height: 40px; vertical-align: top; } #weather-widget .weather-list > span.weather-list-icon { width: 35px; height: 35px; background-repeat: no-repeat; background-position: center; background-size: 30px 30px; } #weather-widget .weather-list > span.weather-list-icon.wi-0 { background-image: url('../img/icons/weather/0.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-1 { background-image: url('../img/icons/weather/1.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-2 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-3 { background-image: url('../img/icons/weather/3.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-4 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-5 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-6 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-7 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-8 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-9 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-10 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-11 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-12 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-13 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-14 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-15 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-16 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-17 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-18 { background-image: url('../img/icons/weather/18.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-19 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-20 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-21 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-22 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-23 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-24 { background-image: url('../img/icons/weather/24.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-25 { background-image: url('../img/icons/weather/24.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-26 { background-image: url('../img/icons/weather/26.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-27 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-28 { background-image: url('../img/icons/weather/28.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-29 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-30 { background-image: url('../img/icons/weather/28.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-31 { background-image: url('../img/icons/weather/31.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-32 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-33 { background-image: url('../img/icons/weather/31.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-34 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-35 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-36 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-37 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-38 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-39 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-40 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-41 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-42 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-43 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-44 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-45 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-46 { background-image: url('../img/icons/weather/18.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-47 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span > i { line-height: 100%; font-size: 39px; } /* * Pie Charts */ #pie-charts { background: #fff; } #pie-charts .dash-widget-header { color: rgba(255, 255, 255, 0.9); } /* * Blog Post */ .blog-post .bp-header { position: relative; } .blog-post .bp-header > img { width: 100%; } .blog-post .bp-header .bp-title { background: #3f51b5; width: 100%; padding: 20px; color: #FFF; display: block; } .blog-post .bp-header .bp-title > h2 { color: #FFF; font-weight: 400; margin: 0 0 2px; line-height: 100%; font-size: 21px; } /* * Profile View */ .profile-view { text-align: center; } .profile-view .pv-header { position: relative; height: 145px; width: 100%; background-image: url('../img/headers/sm/4.png'); background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } .profile-view .pv-header > .pv-main { border-radius: 50%; width: 130px; position: absolute; height: 130px; bottom: -50px; left: 50%; margin-left: -65px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-view .pv-body { margin-top: 70px; padding: 0 20px 20px; } .profile-view .pv-body > h2 { margin: 0; line-height: 100%; font-size: 20px; font-weight: 400; } .profile-view .pv-body > small { display: block; color: #8E8E8E; margin: 10px 0 15px; } .profile-view .pv-body .pv-contact, .profile-view .pv-body .pv-follow { padding: 0; list-style: none; } .profile-view .pv-body .pv-contact > li, .profile-view .pv-body .pv-follow > li { display: inline-block; } .profile-view .pv-body .pv-follow { margin: 20px -20px; padding: 10px; background-color: #F7F7F7; border-top: 1px solid #EEE; border-bottom: 1px solid #EEE; } .profile-view .pv-body .pv-follow > li { padding: 0 10px; } .profile-view .pv-body .pv-contact > li { margin: 0 5px; } .profile-view .pv-body .pv-contact > li > .zmdi { line-height: 100%; vertical-align: text-bottom; font-size: 22px; } .profile-view .pv-body .pv-follow-btn { padding: 7px 20px; background: #00bcd4; color: #FFF; border-radius: 3px; text-transform: uppercase; display: block; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-view .pv-body .pv-follow-btn:hover { background: #00a5bb; } .profile-view:hover .pv-main { -webkit-transform: scale(1.2); -ms-transform: scale(1.2); -o-transform: scale(1.2); transform: scale(1.2); } /* * Picture List */ .picture-list .pl-body { padding: 2px; } .picture-list .pl-body [class*="col-"] { padding: 0; padding: 2px; } .picture-list .pl-body [class*="col-"] > a { display: block; } @media (min-width: 768px) { .picture-list .pl-body [class*="col-"] > a { position: relative; } .picture-list .pl-body [class*="col-"] > a:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.3); z-index: 0; border-radius: 0; opacity: 0; filter: alpha(opacity=0); } .picture-list .pl-body [class*="col-"] > a:hover:before, .picture-list .pl-body [class*="col-"] > a.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .picture-list .pl-body [class*="col-"] > a img { width: 100%; } .picture-list .pl-body:before, .picture-list .pl-body:after { content: " "; display: table; } .picture-list .pl-body:after { clear: both; } .picture-list .pl-body:before, .picture-list .pl-body:after { content: " "; display: table; } .picture-list .pl-body:after { clear: both; } /* * Social */ .go-social .card-body { padding: 0 15px 20px; } .go-social .card-body [class*="col-"] { padding: 12px; } .go-social .card-body [class*="col-"] img { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .go-social .card-body [class*="col-"]:hover img { -webkit-transform: scale(1.2); -ms-transform: scale(1.2); -o-transform: scale(1.2); transform: scale(1.2); } /* * Rating */ .rating-list { padding: 0 0 10px; } .rating-list .rl-star { margin-top: 10px; margin-bottom: 4px; } .rating-list .rl-star .zmdi { font-size: 20px; } .rating-list .rl-star .zmdi:not(.active) { color: #ccc; } .rating-list .rl-star .zmdi.active { color: #ff9800; } .rating-list .lv-item .media .zmdi-star { line-height: 100%; font-size: 22px; color: #FF9800; vertical-align: middle; position: relative; top: -2px; left: 6px; } .rating-list .lv-item .media .media-body { padding: 7px 10px 0 5px; } .table { background-color: #ffffff; margin-bottom: 0; } .table > thead > tr > th { background-color: #fff; vertical-align: middle; font-weight: 500; color: #333; border-width: 1px; text-transform: uppercase; } .table [class*="bg-"] > tr > th { color: #fff; border-bottom: 0; } .table [class*="bg-"] + tbody > tr > td { border-top: 0; } .table.table-inner { border: 0; } .table > thead > tr > th:first-child, .table > tbody > tr > th:first-child, .table > tfoot > tr > th:first-child, .table > thead > tr > td:first-child, .table > tbody > tr > td:first-child, .table > tfoot > tr > td:first-child { padding-left: 30px; } .table > thead > tr > th:last-child, .table > tbody > tr > th:last-child, .table > tfoot > tr > th:last-child, .table > thead > tr > td:last-child, .table > tbody > tr > td:last-child, .table > tfoot > tr > td:last-child { padding-right: 30px; } .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > tbody > tr.succes > td, .table > tfoot > tr.succes > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td { border: 0; } .table > tbody > tr:last-child > td, .table > tfoot > tr:last-child > td { padding-bottom: 20px; } .table-striped td, .table-striped th { border: 0 !important; } .table-bordered { border-bottom: 0; border-left: 0; border-right: 0; } .table-bordered > tbody > tr > td, .table-bordered > tbody > tr > th { border-bottom: 0; border-left: 0; } .table-bordered > tbody > tr > td:last-child, .table-bordered > tbody > tr > th:last-child { border-right: 0; } .table-bordered > thead > tr > th { border-left: 0; } .table-bordered > thead > tr > th:last-child { border-right: 0; } .table-vmiddle td { vertical-align: middle !important; } .table-responsive { border: 0; } #todo-lists { background: #ffc107; color: #fff; margin-bottom: 30px; font-family: 'shadowsintolight', cursive; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } .tl-header { position: relative; padding: 25px; } .tl-header > h2 { margin: 0; color: #fff; line-height: 100%; } .tl-header > small { font-size: 17px; display: block; margin-top: 3px; } .tl-header .actions { position: absolute; right: 10px; padding: 0; list-style: none; top: 15px; } .tl-header .actions > li { display: inline-block; vertical-align: baseline; } .tl-body { min-height: 300px; position: relative; padding: 20px 10px 20px 25px; background: rgba(0, 0, 0, 0.03); } .tl-body .media-body { padding-top: 3px; font-size: 18px; } .tl-body .checkbox { margin-bottom: 15px; } .tl-body .checkbox span { display: inline-block; margin-top: -3px; } .tl-body .checkbox input:checked + i + span { text-decoration: line-through; } .tl-body .checkbox .input-helper:before { border-color: rgba(255, 255, 255, 0.8); border-width: 2px; } .tl-body .checkbox .input-helper:after { border-color: #fff; } #add-tl-item { width: 50px; height: 50px; border-radius: 50%; position: absolute; background: #fff; top: -25px; right: 23px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-transition-duration: 200ms; transition-duration: 200ms; } #add-tl-item .add-new-item { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } #add-tl-item .add-tl-body { overflow: hidden; opacity: 0; filter: alpha(opacity=0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } #add-tl-item .add-tl-body textarea { padding: 25px 25px 45px; resize: none; width: 100%; font-size: 24px; color: #ffc107; position: absolute; height: 100%; border: 0; outline: none; } #add-tl-item:not(.toggled) { overflow: hidden; } #add-tl-item:not(.toggled) .add-new-item { position: relative; z-index: 1; display: inline-block; width: 50px; height: 50px; background-repeat: no-repeat; background-position: center; cursor: pointer; text-align: center; font-size: 23px; color: #ff9800; line-height: 50px; } #add-tl-item.toggled { width: calc(100% - 47px); height: calc(100% - 25px); border-radius: 2px; top: 0; z-index: 1; box-shadow: 0 5px 8px rgba(0, 0, 0, 0.2); max-height: 300px; overflow: visible; } #add-tl-item.toggled .add-new-item { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); height: 0; overflow: hidden; float: left; } #add-tl-item.toggled .add-tl-body { opacity: 1; filter: alpha(opacity=100); } #add-tl-item.toggled .add-tl-body .add-tl-actions { position: absolute; bottom: 0; width: 100%; padding: 5px 10px; border-top: 1px solid #EEE; z-index: 1; } #add-tl-item.toggled .add-tl-body .add-tl-actions > a { font-size: 25px; padding: 0 6px; text-align: center; height: 40px; width: 40px; display: inline-block; line-height: 41px; border-radius: 50%; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } #add-tl-item.toggled .add-tl-body .add-tl-actions > a:hover { background-color: #eee; } #add-tl-item.toggled .add-tl-body .add-tl-actions [data-tl-action="dismiss"] { color: #f44336; } #add-tl-item.toggled .add-tl-body .add-tl-actions [data-tl-action="save"] { color: #4caf50; } .btn { border: 0; text-transform: uppercase; } .btn[class*="bgm-"]:not(.bgm-white) { color: #fff; } .btn .caret { margin-top: -3px; } .btn:not(.btn-link) { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .btn-group:not(.bootstrap-select), .btn-group-vertical:not(.bootstrap-select) { box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.3); } .btn-group .btn, .btn-group-vertical .btn, .btn-group .btn:active, .btn-group-vertical .btn:active, .btn-group .btn:focus, .btn-group-vertical .btn:focus, .btn-group .btn-group, .btn-group-vertical .btn-group { box-shadow: none !important; } .btn-group .btn, .btn-group-vertical .btn { margin: 0; } .btn-xs, .btn-group-xs > .btn { padding: 2px 5px; font-size: 11px; line-height: 1.5; border-radius: 2px; } .btn-link { color: #797979; text-decoration: none; border-radius: 2px; } .btn-link:hover { color: #0a0a0a; } .btn-link:hover, .btn-link:active, .btn-link:focus { text-decoration: none; } .btn-inverse { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:focus, .btn-inverse.focus { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:hover { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active:hover, .btn-inverse.active:hover, .open > .dropdown-toggle.btn-inverse:hover, .btn-inverse:active:focus, .btn-inverse.active:focus, .open > .dropdown-toggle.btn-inverse:focus, .btn-inverse:active.focus, .btn-inverse.active.focus, .open > .dropdown-toggle.btn-inverse.focus { color: #ffffff; background-color: #1a1a1a; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { background-image: none; } .btn-inverse.disabled:hover, .btn-inverse[disabled]:hover, fieldset[disabled] .btn-inverse:hover, .btn-inverse.disabled:focus, .btn-inverse[disabled]:focus, fieldset[disabled] .btn-inverse:focus, .btn-inverse.disabled.focus, .btn-inverse[disabled].focus, fieldset[disabled] .btn-inverse.focus { background-color: #454545; border-color: transparent; } .btn-inverse .badge { color: #454545; background-color: #ffffff; } .btn-inverse:hover, .btn-inverse:focus, .btn-inverse.focus, .btn-inverse:active, .open > .dropdown-toggle.btn-inverse { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:hover:hover, .btn-inverse:focus:hover, .btn-inverse.focus:hover, .btn-inverse:active:hover, .open > .dropdown-toggle.btn-inverse:hover, .btn-inverse:hover:focus, .btn-inverse:focus:focus, .btn-inverse.focus:focus, .btn-inverse:active:focus, .open > .dropdown-toggle.btn-inverse:focus, .btn-inverse:hover.focus, .btn-inverse:focus.focus, .btn-inverse.focus.focus, .btn-inverse:active.focus, .open > .dropdown-toggle.btn-inverse.focus { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { background-image: none; } .btn-inverse.disabled, .btn-inverse[disabled], fieldset[disabled] .btn-inverse, .btn-inverse.disabled:hover, .btn-inverse[disabled]:hover, fieldset[disabled] .btn-inverse:hover, .btn-inverse.disabled:focus, .btn-inverse[disabled]:focus, fieldset[disabled] .btn-inverse:focus, .btn-inverse.disabled.focus, .btn-inverse[disabled].focus, fieldset[disabled] .btn-inverse.focus, .btn-inverse.disabled:active, .btn-inverse[disabled]:active, fieldset[disabled] .btn-inverse:active { background-color: #454545; border-color: transparent; } .btn-inverse .badge { color: #454545; background-color: #ffffff; } .btn-icon { border-radius: 50%; width: 40px; line-height: 42px; height: 40px; padding: 0; text-align: center; } .btn-icon .zmdi { font-size: 17px; } .btn-icon-text > .zmdi { font-size: 15px; vertical-align: top; display: inline-block; margin-top: 2px; line-height: 100%; margin-right: 5px; } .btn-float { width: 50px; height: 50px; border-radius: 50%; line-height: 45px !important; } .btn-float:not(.m-btn) { position: absolute !important; } .btn-float i { font-size: 23px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 500ms; transition-duration: 500ms; } .btn-float:hover i { -webkit-transform: rotate(360deg); -ms-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } .btn-float:not(.bgm-white):not(.bgm-gray) > i { color: #fff; } .btn-float:not(.bgm-white):not(.bgm-gray) > i { color: #fff; } .btn-float.bgm-white > i, .btn-float.bgm-gray > i { color: #333; } .open .btn { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } .open .btn:focus, .open .btn:active { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } /* * Material Design Add button */ .m-btn { z-index: 1; bottom: 40px; right: 40px; position: fixed !important; } label { font-weight: 500; } /* * Reset Focus and Active shadows */ input:active, input:focus { outline: 0; box-shadow: none !important; } .form-control { box-shadow: none !important; resize: none; } .form-control:active, .form-control:focus { box-shadow: none; } .form-control:not(.fc-alt) { border-left: 0; border-right: 0; border-top: 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; padding: 0; } .form-control:not(.fc-alt).auto-size { padding-top: 6px; } /* * Checkbox and Radio */ .checkbox label, .radio label { padding-left: 30px; position: relative; } .checkbox input, .radio input { top: 0; left: 0; margin-left: 0 !important; z-index: 1; cursor: pointer; opacity: 0; filter: alpha(opacity=0); margin-top: 0; } .checkbox .input-helper:before, .radio .input-helper:before, .checkbox .input-helper:after, .radio .input-helper:after { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; position: absolute; content: ""; } .checkbox .input-helper:before, .radio .input-helper:before { left: 0; border: 1px solid #ccc; } .checkbox.disabled, .radio.disabled { opacity: 0.6; filter: alpha(opacity=60); } .checkbox input { width: 17px; height: 17px; } .checkbox input:checked + .input-helper:before { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .checkbox input:checked + .input-helper:after { -webkit-transform: scale(1) rotate(-50deg); -ms-transform: scale(1) rotate(-50deg); -o-transform: scale(1) rotate(-50deg); transform: scale(1) rotate(-50deg); opacity: 1; filter: alpha(opacity=100); } .checkbox .input-helper:before { top: 0; width: 17px; height: 17px; } .checkbox .input-helper:after { opacity: 0; filter: alpha(opacity=0); -webkit-transform: scale(0) rotate(80deg); -ms-transform: scale(0) rotate(80deg); -o-transform: scale(0) rotate(80deg); transform: scale(0) rotate(80deg); width: 22px; height: 9px; border-bottom: 2px solid #009688; border-left: 2px solid #009688; border-bottom-left-radius: 2px; left: -1px; top: 1px; } .radio input { width: 19px; height: 19px; } .radio input:checked + .input-helper:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .radio .input-helper:before { top: -1px; width: 19px; height: 19px; border-radius: 50%; } .radio .input-helper:after { width: 11px; height: 11px; background: #009688; border-radius: 50%; top: 3px; left: 4px; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .checkbox-inline, .radio-inline { vertical-align: top; margin-top: 0; padding-left: 25px; } /* * Select */ html:not(.ie9) .select { position: relative; } html:not(.ie9) .select:before { position: absolute; top: -1px; content: ""; height: calc(100% - 1px); width: 30px; background-color: #FFF; background-position: right calc(100% - 7px); background-repeat: no-repeat; background-image: url("../img/select.png"); pointer-events: none; z-index: 5; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { html:not(.ie9) .select:before { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } html:not(.ie9) .select:not(.fg-line):before { right: 0; } html:not(.ie9) .select.fg-line:before { right: 10px; } /* * Input Group Addon */ .input-group:not(.input-group-lg):not(.input-group-sm) .input-group-addon { font-size: 15px; } .input-group-addon { border-width: 0px 0px 1px 0px; min-width: 42px; } .input-group-addon > .zmdi { position: relative; top: 3px; } /* * Input Feilds */ .fg-line { position: relative; vertical-align: top; } .fg-line:not(.form-group) { display: inline-block; width: 100%; } .fg-line .form-control:disabled { color: #9d9d9d; background: transparent; } .fg-line:not(.disabled):after, .fg-line:not(.readonly):after { position: absolute; z-index: 3; bottom: 0; left: 0; height: 2px; width: 0; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .fg-line:not([class*=has-]):after { background: #2196f3; } .fg-line.readonly .form-control { color: #9d9d9d; background: transparent; } .fg-line.fg-toggled:after { width: 100%; } .fg-float { margin-top: 2px; position: relative; } .fg-float .form-control { position: relative; background: transparent; z-index: 1; } .fg-float .form-control::-moz-placeholder { color: #ffffff; opacity: 1; } .fg-float .form-control:-ms-input-placeholder { color: #ffffff; } .fg-float .form-control::-webkit-input-placeholder { color: #ffffff; } .fg-float .fg-label { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; position: absolute; top: 5px; font-weight: 400; color: #959595; pointer-events: none; z-index: 0; left: 0; white-space: nowrap; } .fg-float .fg-toggled .fg-label { top: -20px; font-size: 11px; } .control-label { font-weight: normal; } /* * Toggle Switch */ .toggle-switch { display: inline-block; vertical-align: top; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .toggle-switch .ts-label { display: inline-block; margin: 0 20px 0 0; vertical-align: top; -webkit-transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); } .toggle-switch .ts-helper { display: inline-block; position: relative; width: 40px; height: 16px; border-radius: 8px; background: rgba(0, 0, 0, 0.26); -webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); vertical-align: middle; cursor: pointer; } .toggle-switch .ts-helper:before { content: ''; position: absolute; top: -4px; left: -4px; width: 24px; height: 24px; background: #fafafa; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); border-radius: 50%; webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } .toggle-switch:not(.disabled) .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(128, 128, 128, 0.1); } .toggle-switch input { position: absolute; z-index: 1; width: 46px; margin: 0 0 0 -4px; height: 24px; opacity: 0; filter: alpha(opacity=0); cursor: pointer; } .toggle-switch input:checked + .ts-helper:before { left: 20px; } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper { background: rgba(0, 150, 136, 0.5); } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper:before { background: #009688; } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(0, 150, 136, 0.2); } .toggle-switch.disabled { opacity: 0.6; filter: alpha(opacity=60); } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper { background: rgba(244, 67, 54, 0.5); } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper:before { background: #f44336; } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(244, 67, 54, 0.2); } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper { background: rgba(33, 150, 243, 0.5); } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper:before { background: #2196f3; } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(33, 150, 243, 0.2); } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper { background: rgba(255, 193, 7, 0.5); } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper:before { background: #ffc107; } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(255, 193, 7, 0.2); } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper { background: rgba(156, 39, 176, 0.5); } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper:before { background: #9c27b0; } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(156, 39, 176, 0.2); } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper { background: rgba(233, 30, 99, 0.5); } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper:before { background: #e91e63; } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(233, 30, 99, 0.2); } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper { background: rgba(205, 220, 57, 0.5); } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper:before { background: #cddc39; } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(205, 220, 57, 0.2); } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper { background: rgba(0, 188, 212, 0.5); } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper:before { background: #00bcd4; } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(0, 188, 212, 0.2); } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper { background: rgba(76, 175, 80, 0.5); } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper:before { background: #4caf50; } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(76, 175, 80, 0.2); } /* * IE 9 Placeholder */ .ie9-placeholder { color: #888 !important; font-weight: normal; } /* * Validation */ .has-error .checkbox .input-helper:before { border-color: #f99d97; } .has-error .checkbox .input-helper:after { border-bottom-color: #f77066; border-left-color: #f77066; } .has-error .fg-line:after { background: #f44336; } .has-success .checkbox .input-helper:before { border-color: #92cf94; } .has-success .checkbox .input-helper:after { border-bottom-color: #6ec071; border-left-color: #6ec071; } .has-success .fg-line:after { background: #4caf50; } .has-warning .checkbox .input-helper:before { border-color: #ffc166; } .has-warning .checkbox .input-helper:after { border-bottom-color: #ffad33; border-left-color: #ffad33; } .has-warning .fg-line:after { background: #ff9800; } .pagination { border-radius: 0; } .pagination > li { margin: 0 2px; display: inline-block; vertical-align: top; } .pagination > li > a, .pagination > li > span { border-radius: 50% !important; padding: 0; width: 40px; height: 40px; line-height: 38px; text-align: center; font-size: 14px; z-index: 1; position: relative; cursor: pointer; } .pagination > li > a > .zmdi, .pagination > li > span > .zmdi { font-size: 22px; line-height: 39px; } .pagination > li.disabled { opacity: 0.5; filter: alpha(opacity=50); } /* * Listview Pagination */ .lv-pagination { width: 100%; text-align: center; padding: 40px 0; border-top: 1px solid #F0F0F0; margin-top: 0; margin-bottom: 0; } /* * Pager */ .pager li > a, .pager li > span { padding: 5px 10px 6px; color: #7e7e7e; } .popover { box-shadow: 0 2px 30px rgba(0, 0, 0, 0.2); } .popover-title { border-bottom: 0; padding: 15px; font-size: 12px; text-transform: uppercase; } .popover-title + .popover-content { padding-top: 0; } .popover-content { padding: 15px; } .popover-content p { margin-bottom: 0; } .fw-container .tab-content { padding: 25px 0; } .fw-container .fw-footer { text-align: center; margin: 30px 0 0; width: 100%; border-top: 2px solid #eee; padding: 15px 0; } .alert { padding-left: 30px; font-size: 13px; } .alert span { cursor: pointer; } .alert:not(.alert-dismissible) { padding-right: 30px; } .alert.alert-dismissable { padding-right: 44px; } .alert-inverse { background-color: #333333; border-color: transparent; color: #ffffff; } .alert-inverse hr { border-top-color: rgba(0, 0, 0, 0); } .alert-inverse .alert-link { color: #e6e6e6; } .growl-animated.alert-inverse { box-shadow: 0 0 5px rgba(51, 51, 51, 0.5); } .growl-animated.alert-info { box-shadow: 0 0 5px rgba(33, 150, 243, 0.5); } .growl-animated.alert-success { box-shadow: 0 0 5px rgba(76, 175, 80, 0.5); } .growl-animated.alert-warning { box-shadow: 0 0 5px rgba(255, 193, 7, 0.5); } .growl-animated.alert-danger { box-shadow: 0 0 5px rgba(244, 67, 54, 0.5); } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #e0e0e0; box-shadow: 0 0 6px #EAEAEA; } /* * Lightbox */ .lightbox .lightbox-item > img { width: 100%; border-radius: 2px; } @media (min-width: 768px) { .lightbox .lightbox-item { position: relative; } .lightbox .lightbox-item:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.1); z-index: 0; border-radius: 0; opacity: 0; filter: alpha(opacity=0); } .lightbox .lightbox-item:hover:before, .lightbox .lightbox-item.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .lightbox .lightbox-item:hover { cursor: pointer; } .lightbox [data-src]:before, .lightbox [data-src]:after { content: " "; display: table; } .lightbox [data-src]:after { clear: both; } .lightbox [data-src]:before, .lightbox [data-src]:after { content: " "; display: table; } .lightbox [data-src]:after { clear: both; } .lightbox .lightbox-item:not(.p-item) { position: relative; } /* * Carousel */ .carousel .carousel-control { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; opacity: 0; filter: alpha(opacity=0); } .carousel .carousel-control .zmdi { position: absolute; top: 50%; left: 50%; line-height: 100%; } @media screen and (min-width: 768px) { .carousel .carousel-control .zmdi { font-size: 60px; width: 60px; height: 60px; margin-top: -30px; margin-left: -30px; } } @media screen and (max-width: 991px) { .carousel .carousel-control .zmdi { width: 24px; height: 24px; margin-top: -12px; margin-left: -12px; } } .carousel:hover .carousel-control { opacity: 1; filter: alpha(opacity=100); } .carousel .carousel-caption { background: rgba(0, 0, 0, 0.6); left: 0; right: 0; bottom: 0; width: 100%; padding-bottom: 50px; } .carousel .carousel-caption > h3 { color: #fff; margin: 0 0 5px; font-weight: 300; } .carousel .carousel-caption > p { margin: 0; } @media screen and (max-width: 991px) { .carousel .carousel-caption { display: none; } } .carousel .carousel-indicators { bottom: 10px; margin: 0; left: 0; bottom: 0; width: 100%; padding: 0 0 6px; background: rgba(0, 0, 0, 0.6); } .carousel .carousel-indicators li { border-radius: 0; width: 15px; border: 0; background: #fff; height: 3px; margin: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .carousel .carousel-indicators li.active { width: 25px; height: 3px; background: #ff9800; } .modal .modal-content { box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31); border-radius: 3px; border: 0; } .modal .modal-header { padding: 23px 26px; } .modal .modal-body { padding: 0 26px 10px; } .modal .modal-footer .btn-link { font-size: 14px; color: #000; font-weight: 500; } .modal .modal-footer .btn-link:hover { background-color: #eee; } .modal:not([data-modal-color]) .modal-footer .btn-link { font-weight: 500; } .modal:not([data-modal-color]) .modal-footer .btn-link:hover { background-color: #eee; } .modal[data-modal-color] { color: #fff; } .modal[data-modal-color] .modal-title, .modal[data-modal-color] .modal-footer .btn-link { color: #fff; } .modal[data-modal-color] .modal-footer { background: rgba(0, 0, 0, 0.1); } .modal[data-modal-color] .modal-backdrop { background: #fff; } .modal[data-modal-color] .modal-footer .btn-link { font-weight: 400; } .modal[data-modal-color] .modal-footer .btn-link:hover { background-color: rgba(0, 0, 0, 0.1); } .modal[data-modal-color="blue"] .modal-content { background: #2196f3; } .modal[data-modal-color="cyan"] .modal-content { background: #00bcd4; } .modal[data-modal-color="green"] .modal-content { background: #4caf50; } .modal[data-modal-color="lightgreen"] .modal-content { background: #8bc34a; } .modal[data-modal-color="lightblue"] .modal-content { background: #03a9f4; } .modal[data-modal-color="amber"] .modal-content { background: #ffc107; } .modal[data-modal-color="teal"] .modal-content { background: #009688; } .modal[data-modal-color="orange"] .modal-content { background: #ff9800; } .modal[data-modal-color="bluegray"] .modal-content { background: #607d8b; } .modal[data-modal-color="red"] .modal-content { background: #f44336; } .panel { box-shadow: none; border: 0; } .panel-heading { padding: 0; } .panel-title > a { padding: 10px 15px; display: block; font-size: 13px; } .panel-collapse .panel-heading { position: relative; } .panel-collapse .panel-heading .panel-title > a { padding: 8px 5px 16px 30px; color: #000; position: relative; } .panel-collapse .panel-heading .panel-title > a:after, .panel-collapse .panel-heading .panel-title > a:before { position: absolute; bottom: 0; left: 0; height: 2px; width: 100%; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .panel-collapse .panel-heading .panel-title > a:after { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .panel-collapse .panel-heading:not(.active) .panel-title > a:before { background: #eee; } .panel-collapse .panel-heading:before, .panel-collapse .panel-heading:after { font-family: 'Material-Design-Iconic-Font'; font-size: 17px; position: absolute; left: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; top: 4px; } .panel-collapse .panel-heading:before { content: "\f278"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-heading:after { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); content: "\f273"; } .panel-collapse .panel-heading.active .panel-title > a:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-heading.active:before { -webkit-transform: scale(0) rotate(-90deg); -ms-transform: scale(0) rotate(-90deg); -o-transform: scale(0) rotate(-90deg); transform: scale(0) rotate(-90deg); } .panel-collapse .panel-heading.active:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-body { border-top: 0 !important; padding-left: 5px; padding-right: 5px; } .panel-group:not([data-collapse-color]) .panel-collapse .panel-heading.active .panel-title > a:after { background: #2196f3; } .panel-group[data-collapse-color="red"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #f44336; } .panel-group[data-collapse-color="green"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #4caf50; } .panel-group[data-collapse-color="amber"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #ffc107; } .panel-group[data-collapse-color="teal"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #009688; } .panel-group[data-collapse-color="black"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #000000; } .panel-group[data-collapse-color="cyan"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #00bcd4; } .tooltip-inner { border-radius: 1px; padding: 3px 10px 5px; } .breadcrumb { border-bottom: 1px solid #E5E5E5; border-radius: 0; } .breadcrumb > li > a { color: #A9A9A9; } .breadcrumb > li > a:hover { color: #7c7c7c; } @media (min-width: 768px) { body:not(.sw-toggled) .breadcrumb { padding: 10px 33px 11px; } } @media (min-width: 1199px) { body.sw-toggled .breadcrumb { padding: 10px 33px 11px 280px; } } #messages-main { position: relative; } #messages-main:before, #messages-main:after { content: " "; display: table; } #messages-main:after { clear: both; } #messages-main:before, #messages-main:after { content: " "; display: table; } #messages-main:after { clear: both; } #messages-main .ms-block { padding: 23px 20px 0; } #messages-main .ms-menu { position: absolute; left: 0; top: 0; background: #F8F8F8; border-right: 1px solid #EEE; padding-bottom: 50px; height: 100%; width: 240px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } @media (max-width: 767px) { #messages-main .ms-menu { height: calc(100% - 58px); -webkit-transform: translate3d(-240px, 58px, 0); transform: translate3d(-240px, 58px, 0); opacity: 0; filter: alpha(opacity=0); z-index: 1; } #messages-main .ms-menu.toggled { -webkit-transform: translate3d(0, 58px, 0); transform: translate3d(0, 58px, 0); opacity: 1; filter: alpha(opacity=100); } } #messages-main .ms-menu .lv-item { padding-left: 20px; padding-right: 20px; } #messages-main .ms-menu .lv-item.active { background: #fff; } #messages-main .ms-menu .lv-item:not(.active):hover { background: #F2F2F2; cursor: pointer; } @media (min-width: 768px) { #messages-main .ms-body { padding-left: 240px; } } @media (max-width: 767px) { #messages-main .ms-body { overflow: hidden; } } #messages-main .ms-user:before, #messages-main .ms-user:after { content: " "; display: table; } #messages-main .ms-user:after { clear: both; } #messages-main .ms-user:before, #messages-main .ms-user:after { content: " "; display: table; } #messages-main .ms-user:after { clear: both; } #messages-main .ms-user > img { border-radius: 50%; width: 40px; float: left; } #messages-main .ms-user > div { overflow: hidden; padding: 7px 5px 7px 15px; font-size: 11px; } #ms-menu-trigger { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; float: left; margin: 1px 0 0 -7px; } @media (min-width: 768px) { #ms-menu-trigger { display: none; } } #ms-menu-trigger .line-wrap .line { background-color: #717171; } /* * For Message */ .lv-message .lv-item { padding: 20px; } .lv-message .lv-item.right { text-align: right; } .lv-message .lv-item.right .lv-avatar { margin-right: 0; margin-left: 15px; } .lv-message .lv-item:not(.right) .ms-item { background: #ffc107; color: #fff; } .lv-message .lv-item.right .ms-item { background: #eee; } .lv-avatar { width: 35px; height: 35px; border-radius: 50%; color: #FFF; text-align: center; line-height: 34px; font-size: 15px; margin-right: 15px; padding: 0 !important; text-transform: uppercase; } .lv-avatar > img { width: 35px; height: 35px; border-radius: 50%; vertical-align: top; } .ms-item { padding: 13px 19px 15px; border-radius: 2px; display: inline-block; } @media (min-width: 768px) { .ms-item { max-width: 70%; } } .ms-date { display: block; color: #B3B3B3; margin-top: 7px; } .ms-date > i { font-size: 14px; vertical-align: bottom; line-height: 100%; } .ms-reply { box-shadow: 0 -20px 20px -5px #ffffff; position: relative; margin: 0 !important; } .ms-reply textarea { width: 100%; font-size: 13px; border: 0; padding: 10px 8px; resize: none; height: 60px; } .ms-reply button { position: absolute; top: 0; right: 0; border: 0; height: 100%; width: 60px; font-size: 25px; background: #F5F5F5; color: #2196f3; } .ms-reply button:hover { background: #f2f2f2; } .four-zero-content { background: #fff; padding: 20px; } .four-zero-content:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #EDECEC; content: ""; } .four-zero { background: #00bcd4; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; position: absolute; top: 50%; margin-top: -150px; color: #fff; text-align: center; padding: 15px; height: 300px; width: 500px; left: 50%; margin-left: -250px; } .four-zero h2 { font-size: 130px; } @media (max-width: 767px) { .four-zero { width: calc(100% - 40px); left: 20px; margin-left: 0; height: 260px; margin-top: -130px; } .four-zero h2 { font-size: 90px; } } .four-zero h2 { line-height: 100%; color: #fff; font-weight: 100; } .four-zero small { display: block; font-size: 26px; margin-top: -10px; } .four-zero footer { background: rgba(0, 0, 0, 0.13); position: absolute; left: 0; bottom: 0; width: 100%; padding: 10px; } .four-zero footer > a { font-size: 21px; display: inline-block; color: #FFF; margin: 0 1px; line-height: 40px; width: 40px; height: 40px; background: rgba(0, 0, 0, 0.09); border-radius: 50%; text-align: center; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .four-zero footer > a:hover { background: rgba(0, 0, 0, 0.2); } .login-content { overflow: hidden; height: 100%; } .lc-block { background: #fff; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; width: 500px; display: inline-block; margin-top: 42px; vertical-align: middle; position: relative; } .lc-block:not(.toggled) { display: none; } .lc-block.toggled { -webkit-animation-name: fadeInUp; animation-name: fadeInUp; -webkit-animation-duration: 300ms; animation-duration: 300ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; z-index: 10; } .lc-block:not(.lcb-alt) { padding: 35px 55px 35px; } @media (max-width: 767px) { .lc-block { padding: 15px 35px 25px 20px; width: calc(100% - 60px); } } .lc-block .checkbox { margin: 5px 0 0 42px; text-align: left; } .lc-block:not(.lcb-alt) .btn-login { top: 50%; margin-top: -25px; right: -25px; } .login-navigation { list-style: none; padding: 0; margin: 0; position: absolute; width: 100%; text-align: center; left: 0%; bottom: -45px; } .login-navigation > li { display: inline-block; margin: 0 2px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 150ms; transition-duration: 150ms; cursor: pointer; vertical-align: top; color: #fff; line-height: 16px; min-width: 16px; min-height: 16px; text-transform: uppercase; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .login-navigation > li > span { opacity: 0; filter: alpha(opacity=0); } .login-navigation > li:not(:hover) { font-size: 0px; border-radius: 100%; } .login-navigation > li:hover { border-radius: 10px; padding: 0 5px; font-size: 8px; } .login-navigation > li:hover > span { opacity: 1; filter: alpha(opacity=100); } .lcb-alt { padding: 70px 55px 60px; } .lcb-alt .btn-login { bottom: -25px; left: 50%; margin-left: -25px; } .lcb-alt .login-navigation { bottom: -75px; } .lcb-user { width: 100px; height: 100px; border-radius: 50%; border: 5px solid #fff; position: absolute; top: -50px; left: 50%; margin-left: -50px; box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.18); } body.login-content { text-align: center; } body.login-content:after { content: ""; vertical-align: middle; display: inline-block; width: 1px; height: 100%; } body.login-content:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #00bcd4; content: ""; z-index: 0; } #profile-main { min-height: 500px; position: relative; } #profile-main .pm-overview { overflow-y: auto; } @media (min-width: 1200px) { #profile-main .pm-overview { width: 300px; } } @media (min-width: 768px) and (max-width: 1200px) { #profile-main .pm-overview { width: 250px; } } @media (min-width: 768px) { #profile-main .pm-overview { position: absolute; left: 0; top: 0; height: 100%; background: #f8f8f8; border-right: 1px solid #eee; } } @media (max-width: 767px) { #profile-main .pm-overview { width: 100%; background: #333; text-align: center; } } @media (min-width: 1200px) { #profile-main .pm-body { padding-left: 300px; } } @media (min-width: 768px) and (max-width: 1200px) { #profile-main .pm-body { padding-left: 250px; } } @media (max-width: 767px) { #profile-main .pm-body { padding-left: 0; } } #profile-main .pmo-pic { position: relative; margin: 20px; } @media (min-width: 768px) { #profile-main .pmo-pic img { width: 100%; border-radius: 2px 2px 0 0; } } @media (max-width: 767px) { #profile-main .pmo-pic img { width: 180px; display: inline-block; height: 180px; border-radius: 50%; border: 4px solid #fff; box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } } #profile-main .pmo-pic .pmo-stat { border-radius: 0 0 2px 2px; color: #fff; text-align: center; padding: 30px 5px 0; } @media (min-width: 768px) { #profile-main .pmo-pic .pmo-stat { background: #ffc107; padding-bottom: 15px; } } #profile-main .pmo-pic .pmop-edit { position: absolute; top: 0; left: 0; color: #fff; background: rgba(0, 0, 0, 0.38); text-align: center; padding: 10px 10px 11px; -webkit-transition: opacity; -o-transition: opacity; transition: opacity; -webkit-transition-duration: 250ms; transition-duration: 250ms; } #profile-main .pmo-pic .pmop-edit:hover { background: rgba(0, 0, 0, 0.8); } #profile-main .pmo-pic .pmop-edit i { font-size: 18px; vertical-align: middle; margin-top: -3px; } @media (min-width: 768px) { #profile-main .pmo-pic .pmop-edit { width: 100%; opacity: 0; filter: alpha(opacity=0); } #profile-main .pmo-pic .pmop-edit i { margin-right: 4px; } } #profile-main .pmo-pic:hover .pmop-edit { opacity: 1; filter: alpha(opacity=100); } #profile-main .pmo-pic .pmop-message { position: absolute; bottom: 27px; left: 50%; margin-left: -25px; } #profile-main .pmo-pic .pmop-message .dropdown-menu { padding: 5px 0 55px; left: -90px; width: 228px; height: 150px; top: -74px; -webkit-transform-origin: center; -moz-transform-origin: center; -ms-transform-origin: center; transform-origin: center; } #profile-main .pmo-pic .pmop-message .dropdown-menu textarea { width: 100%; height: 95px; border: 0; resize: none; padding: 10px 19px; } #profile-main .pmo-pic .pmop-message .dropdown-menu button { bottom: 5px; left: 88px; } #profile-main .pmb-block { margin-bottom: 20px; } @media (min-width: 1200px) { #profile-main .pmb-block { padding: 40px 42px 0; } } @media (max-width: 1199px) { #profile-main .pmb-block { padding: 30px 20px 0; } } #profile-main .pmb-block:last-child { margin-bottom: 50px; } #profile-main .pmb-block .pmbb-header { margin-bottom: 25px; position: relative; } #profile-main .pmb-block .pmbb-header .actions { position: absolute; top: -2px; right: 0; } #profile-main .pmb-block .pmbb-header h2 { margin: 0; font-weight: 100; font-size: 20px; } #profile-main .pmb-block .pmbb-edit { position: relative; z-index: 1; display: none; } #profile-main .pmb-block .pmbb-edit, #profile-main .pmb-block .pmbb-view { -webkit-animation-name: fadeIn; animation-name: fadeIn; -webkit-animation-duration: 1000ms; animation-duration: 1000ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; } #profile-main .pmb-block.toggled .pmbb-edit { display: block; } #profile-main .pmb-block.toggled .pmbb-view { display: none; } #profile-main .pmo-block { padding: 25px; } #profile-main .pmo-block > h2 { font-size: 16px; margin: 0 0 15px; } #profile-main .pmo-items .pmob-body { padding: 0 10px; } #profile-main .pmo-items a { display: block; padding: 4px; } #profile-main .pmo-items a img { width: 100%; } .pmo-contact ul { list-style: none; margin: 0; padding: 0; } .pmo-contact ul li { position: relative; padding: 8px 0 8px 35px; } .pmo-contact ul li i { font-size: 18px; vertical-align: top; line-height: 100%; position: absolute; left: 0; width: 18px; text-align: center; } .pmo-map { margin: 20px -21px -18px; display: block; } .pmo-map img { width: 100%; } @media (max-width: 767px) { .c-timeline { background: #edecec; box-shadow: none; } .c-timeline .tab-nav { background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } .timeline { position: relative; } @media (min-width: 768px) { .timeline { padding: 50px; padding-left: 100px; } } @media (max-width: 767px) { .timeline { margin-top: 30px; } } .t-view { border: 1px solid #eee; position: relative; margin-bottom: 35px; } @media (max-width: 767px) { .t-view { background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } .t-view .tv-header { padding: 16px 18px; border-bottom: 1px solid #eee; background: #F9F9F9; } .t-view .tv-header .actions { position: absolute; top: 5px; right: 10px; } .t-view .tv-body { padding: 23px 25px; } .t-view .tv-body .tvb-lightbox { margin: 0 -8px 15px; } .t-view .tv-body .tvb-lightbox [data-src] { padding: 0 5px; margin-bottom: 5px; } .t-view .tvh-user { display: block; } .t-view .tvh-user img { width: 46px; height: 46px; border-radius: 50%; } .t-view:before { position: absolute; width: 40px; height: 40px; border-radius: 50%; left: -70px; top: 0; border: 3px solid #FFF; text-align: center; font-size: 16px; line-height: 34px; color: #FFF; font-family: 'Material-Design-Iconic-Font'; z-index: 1; } .t-view:after { content: ""; position: absolute; top: 0; left: -50px; width: 1px; height: calc(100% + 37px); } .t-view[data-tv-type="text"]:before { content: "\f24f"; background: #00bcd4; box-shadow: 0 0 0 1px #00bcd4; } .t-view[data-tv-type="text"]:after { background: #00bcd4; } .t-view[data-tv-type="image"]:before { content: "\f17f"; background: #4caf50; box-shadow: 0 0 0 1px #4caf50; } .t-view[data-tv-type="image"]:after { background: #4caf50; } .t-view[data-tv-type="video"]:before { content: "\f3a9"; background: #ffc107; box-shadow: 0 0 0 1px #ffc107; } .t-view[data-tv-type="video"]:after { background: #ffc107; } .t-view .tvb-stats { list-style: none; padding: 0; margin: 10px 0 20px; } .t-view .tvb-stats > li { display: inline-block; padding: 5px 10px 6px; border: 1px solid #ccc; margin-right: 2px; } .t-view .tvb-stats > li i { font-size: 15px; line-height: 100%; vertical-align: top; margin-top: 2px; } .t-view .tvb-stats > li.tvbs-comments { border-color: #4caf50; color: #4caf50; } .t-view .tvb-stats > li.tvbs-likes { border-color: #03a9f4; color: #03a9f4; } .t-view .tvb-stats > li.tvbs-views { border-color: #ff9800; color: #ff9800; } .tv-comments .tvc-lists { padding: 0; list-style: none; margin: 0; } .tv-comments .tvc-lists > li { padding: 15px 20px; margin: 0; border-top: 1px solid #eee; } .tvc-more { color: #333; display: block; margin-bottom: -10px; } .tvc-more:hover { color: #000; } .tvc-more i { vertical-align: middle; margin-right: 5px; } .p-header { position: relative; margin: 0 -7px; } .p-header .actions { position: absolute; top: -18px; right: 0; } .p-menu { list-style: none; padding: 0 5px; margin: 0 0 30px; } .p-menu > li { display: inline-block; vertical-align: top; } .p-menu > li > a { display: block; padding: 5px 20px 5px 0; font-weight: 500; text-transform: uppercase; font-size: 15px; } .p-menu > li > a > i { margin-right: 4px; font-size: 20px; vertical-align: middle; margin-top: -5px; } .p-menu > li:not(.active) > a { color: #4285F4; } .p-menu > li:not(.active) > a:hover { color: #333; } .p-menu > li.active > a { color: #000; } @media (max-width: 991px) { .p-menu .pm-search { margin: 20px 2px 30px; display: block; } .p-menu .pm-search input[type="text"] { width: 100%; border: 1px solid #ccc; } } .p-menu .pms-inner { margin: -2px 0 0; position: relative; top: -2px; overflow: hidden; white-space: nowrap; } .p-menu .pms-inner i { vertical-align: top; font-size: 20px; line-height: 100%; position: absolute; left: 9px; top: 8px; color: #333; } .p-menu .pms-inner input[type="text"] { height: 35px; border-radius: 2px; padding: 0 10px 0 40px; } @media (min-width: 768px) { .p-menu .pms-inner input[type="text"] { border: 1px solid #fff; width: 50px; background: transparent; position: relative; z-index: 1; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .p-menu .pms-inner input[type="text"]:focus { border-color: #DFDFDF; width: 200px; } } .photos { margin: 2px 0 0; } .photos .lightbox { margin: 0 -8px; } .photos:not(.p-timeline) [data-src] { padding: 3px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 150ms; transition-duration: 150ms; } .p-timeline { position: relative; padding-left: 80px; margin-bottom: 75px; } .p-timeline [data-src] { float: left; width: 70px; height: 70px; margin: 0 3px 3px 0; } .p-timeline:last-child .pt-line:before { height: 100%; } .ptb-title { font-size: 15px; font-weight: 400; margin-bottom: 20px; } .pt-line { position: absolute; left: 0; top: 0; height: 100%; line-height: 14px; } .pt-line:before, .pt-line:after { content: ""; position: absolute; } .pt-line:before { width: 1px; height: calc(100% + 63px); background: #E2E2E2; top: 14px; right: -20px; } .pt-line:after { top: 2px; right: -26px; width: 13px; height: 13px; border: 1px solid #C1C1C1; border-radius: 50%; } .contacts:not(.c-profile) { padding: 0 8px; } .contacts > [class*="col-"] { padding: 0 10px; } .contacts .c-item { border: 1px solid #e2e2e2; border-radius: 2px; margin-bottom: 24px; } .contacts .c-item .ci-avatar { display: block; } .contacts .c-item .ci-avatar img { width: 100%; border-radius: 2px 2px 0 0; } .contacts .ci-avatar { margin: -1px -1px 0; } .contacts .c-info { text-align: center; margin-top: 15px; padding: 0 5px; } .contacts .c-info strong { color: #000; font-size: 14px; font-weight: 500; } .contacts .c-info small { color: #999; margin-top: 3px; } .contacts .c-info strong, .contacts .c-info small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; } .contacts .c-footer { border-top: 1px solid #e2e2e2; margin-top: 18px; } .contacts .c-footer > button { padding: 4px 10px 3px; display: block; width: 100%; text-align: center; color: #333; font-weight: 500; border-radius: 2px; background: #fff; border: 0; } .contacts .c-footer > button > i { font-size: 16px; vertical-align: middle; margin-top: -3px; } .z-depth-1 { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .z-depth-1-top { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); } .z-depth-1-bottom { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16); } .z-depth-2 { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } .z-depth-2-top { box-shadow: 0 6px 20px rgba(0, 0, 0, 0.19); } .z-depth-2-bottom { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2); } .z-depth-3 { box-shadow: 0 12px 15px rgba(0, 0, 0, 0.24), 0 17px 50px rgba(0, 0, 0, 0.19); } .z-depth-3-top { box-shadow: 0 17px 50px rgba(0, 0, 0, 0.19); } .z-depth-3-bottom { box-shadow: 0 12px 15px rgba(0, 0, 0, 0.24); } .z-depth-4 { box-shadow: 0 16px 28px rgba(0, 0, 0, 0.22), 0 25px 55px rgba(0, 0, 0, 0.21); } .z-depth-4-top { box-shadow: 0 25px 55px rgba(0, 0, 0, 0.21); } .z-depth-4-bottom { box-shadow: 0 16px 28px rgba(0, 0, 0, 0.22); } .z-depth-5 { box-shadow: 0 27px 24px rgba(0, 0, 0, 0.2), 0 40px 77px rgba(0, 0, 0, 0.22); } .z-depth-5-top { box-shadow: 0 40px 77px rgba(0, 0, 0, 0.22); } .z-depth-5-bottom { box-shadow: 0 27px 24px rgba(0, 0, 0, 0.2); } .z-depth-animation .z-depth-1, .z-depth-animation .z-depth-2, .z-depth-animation .z-depth-3, .z-depth-animation .z-depth-4, .z-depth-animation .z-depth-5 { transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } /* * Block Header * Used for Heading outside the Cards. */ .block-header { margin-bottom: 25px; position: relative; } @media screen and (min-width: 768px) { .block-header { padding: 0 22px; } } @media screen and (max-width: 991px) { .block-header { padding: 0 18px; } } .block-header > h2 { font-size: 15px; color: #777; margin: 0; font-weight: 400; text-transform: uppercase; } .block-header > h2 > small { display: block; text-transform: none; margin-top: 8px; margin-bottom: 20px; color: #9E9E9E; line-height: 140%; } .block-header .actions { position: absolute; right: 10px; top: -5px; z-index: 4; } /* * Header Actions */ .actions { list-style: none; padding: 0; z-index: 3; margin: 0; } .actions > li { display: inline-block; vertical-align: baseline; } .actions > li > a, .actions > a { width: 30px; height: 30px; display: inline-block; text-align: center; padding-top: 5px; } .actions > li > a > i, .actions > a > i { color: #adadad; font-size: 20px; } .actions > li > a:hover > i, .actions > a:hover > i { color: #000; } @media (min-width: 768px) { .actions > li > a, .actions > a { position: relative; } .actions > li > a:before, .actions > a:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.1); z-index: 0; border-radius: 50%; opacity: 0; filter: alpha(opacity=0); } .actions > li > a:hover:before, .actions > a:hover:before, .actions > li > a.open:before, .actions > a.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .actions > li.open > a > i, .actions.open > a > i { color: #000; } .actions > li.open > a:before, .actions.open > a:before { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .actions.actions-alt > li > a > i { color: #fff; } .actions.actions-alt > li > a > i:hover { color: #fff; } .actions.actions-alt > li.open > a > i { color: #fff; } .actions.open { z-index: 3; } /* * Collapse Menu Icons */ .line-wrap { width: 18px; height: 12px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; margin: 12px 20px; } .line-wrap .line { width: 18px; height: 2px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .line-wrap .line.center { margin: 3px 0; } .open .line-wrap { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); } .open .line-wrap .line.top { width: 12px; transform: translateX(8px) translateY(1px) rotate(45deg); -webkit-transform: translateX(8px) translateY(1px) rotate(45deg); } .open .line-wrap .line.bottom { width: 12px; transform: translateX(8px) translateY(-1px) rotate(-45deg); -webkit-transform: translateX(8px) translateY(-1px) rotate(-45deg); } /* * Load More */ .load-more { text-align: center; margin-top: 30px; } .load-more a { padding: 5px 10px 3px; display: inline-block; background-color: #f44336; color: #FFF; border-radius: 2px; white-space: nowrap; } .load-more a i { font-size: 20px; vertical-align: middle; position: relative; margin-top: -2px; } .load-more a:hover { background-color: #ea1c0d; } /* * Page Loader */ html:not(.ismobile) .page-loader { background: #fff; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; } html:not(.ismobile) .page-loader .preloader { width: 50px; position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -55px; -webkit-animation-name: fadeIn; animation-name: fadeIn; -webkit-animation-duration: 3000ms; animation-duration: 3000ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; } html:not(.ismobile) .page-loader .preloader p { white-space: nowrap; position: relative; left: -9px; top: 22px; color: #CCC; } html.ismobile .page-loader { display: none; } .ie-warning { position: fixed; top: 0; left: 0; z-index: 9999; background: #000000; width: 100%; height: 100%; text-align: center; color: #fff; font-family: "Courier New", Courier, monospace; padding: 50px 0; } .ie-warning p { font-size: 17px; } .ie-warning .iew-container { min-width: 1024px; width: 100%; height: 200px; background: #fff; margin: 50px 0; } .ie-warning .iew-download { list-style: none; padding: 30px 0; margin: 0 auto; width: 720px; } .ie-warning .iew-download > li { float: left; vertical-align: top; } .ie-warning .iew-download > li > a { display: block; color: #000; width: 140px; font-size: 15px; padding: 15px 0; } .ie-warning .iew-download > li > a > div { margin-top: 10px; } .ie-warning .iew-download > li > a:hover { background-color: #eee; } #footer { position: absolute; bottom: 0; text-align: center; width: 100%; height: 110px; color: #a2a2a2; padding-top: 35px; padding-bottom: 15px; } #footer .f-menu { display: block; width: 100%; padding-left: 0; list-style: none; margin-left: -5px; margin-top: 8px; } #footer .f-menu > li { display: inline-block; padding-left: 5px; padding-right: 5px; } #footer .f-menu > li > a { color: #a2a2a2; } #footer .f-menu > li > a:hover { color: #777; } @media (min-width: 1199px) { body.sw-toggled #footer { padding-left: 268px; } } .pt-inner { text-align: center; } .pt-inner .pti-header { padding: 45px 10px 70px; color: #fff; position: relative; margin-bottom: 15px; } .pt-inner .pti-header > h2 { margin: 0; line-height: 100%; color: #fff; font-weight: 100; font-size: 50px; } .pt-inner .pti-header > h2 small { color: #fff; letter-spacing: 0; vertical-align: top; font-size: 16px; font-weight: 100; } .pt-inner .pti-header .ptih-title { background-color: rgba(0, 0, 0, 0.1); padding: 8px 10px 9px; text-transform: uppercase; margin: 0 -10px; position: absolute; width: 100%; bottom: 0; } .pt-inner .pti-body { padding: 0 23px; } .pt-inner .pti-body .ptib-item { padding: 15px 0; font-weight: 400; } .pt-inner .pti-body .ptib-item:not(:last-child) { border-bottom: 1px solid #eee; } .pt-inner .pti-footer { padding: 10px 20px 30px; } .pt-inner .pti-footer > a { width: 60px; height: 60px; border-radius: 50%; text-align: center; color: #fff; display: inline-block; line-height: 60px; font-size: 30px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .pt-inner .pti-footer > a:hover { opacity: 0.85; filter: alpha(opacity=85); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .invoice { min-width: 1100px; max-width: 1170px; } .i-logo { width: 150px; } .i-table .highlight { background-color: #eee; border-bottom: 1px solid #e6e6e6; } .i-table td.highlight { font-size: 14px; font-weight: 500; } .wall-attrs { margin-bottom: 0; } .wa-stats { float: left; } .wa-stats > span { margin-right: -1px; padding: 7px 12px; border: 1px solid #E0E0E0; float: left; font-weight: 500; } .wa-stats > span.active { color: #4caf50; } .wa-stats > span:first-child { border-radius: 2px 0 0 2px; } .wa-stats > span:last-child { border-radius: 0 2px 2px 0; } .wa-stats > span > i { line-height: 100%; vertical-align: top; position: relative; top: 2px; font-size: 15px; margin-right: 2px; } .wa-users { float: right; padding: 0 !important; margin-right: -5px; } .wa-users > a { display: inline-block; margin-left: 2px; } .wa-users > a > img { width: 33px; height: 33px; border-radius: 50%; } .wa-users > a > img:hover { opacity: 0.85; filter: alpha(opacity=85); } .wcc-inner { border: 1px solid #E4E4E4; padding: 10px 15px; resize: none; border-radius: 2px; background: #fff; color: #9A9A9A; cursor: pointer; } .wcci-text { border: 0; display: block; width: 100%; resize: none; padding: 0; } .wall-comment-list { padding: 20px; background: #f7f7f7; } .wall-comment-list .media { position: relative; } .wall-comment-list .media:hover .actions { display: block; } .wall-comment-list .actions { display: none; position: absolute; right: -20px; top: -1px; } .wcl-list + .wcl-form { margin-top: 25px; } .wp-text { border: 0; padding: 0; display: block; width: 100%; resize: none; } .wp-media { background: #f7f7f7; border: 1px solid #E4E4E4; padding: 12px 15px; margin-top: 25px; text-align: center; } .wpb-actions { background: #f7f7f7; margin: 0; padding: 10px 20px; } .wpb-actions > li:not(.pull-right) { float: left; } [data-wpba="image"] { color: #4caf50; } [data-wpba="image"]:hover { color: #449d48; } [data-wpba="video"] { color: #ff9800; } [data-wpba="video"]:hover { color: #e68900; } [data-wpba="link"] { color: #00bcd4; } [data-wpba="link"]:hover { color: #00a5bb; } .wpba-attrs > ul > li { padding: 0; margin-right: 5px; } .wpba-attrs > ul > li > a { display: block; width: 22px; } .wpba-attrs > ul > li > a > i { font-size: 20px; } .wpba-attrs > ul > li.active i { color: #333; } .wall-img-preview { text-align: center; } @media screen and (min-width: 768px) { .wall-img-preview { margin: 0 -23px 20px; } } @media screen and (max-width: 991px) { .wall-img-preview { margin: 0 -16px 20px; } } .wall-img-preview .wip-item { display: block; float: left; position: relative; overflow: hidden; border: 2px solid #fff; background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } .wall-img-preview .wip-item > img { display: none; } .wall-img-preview .wip-item:first-child:nth-last-child(2), .wall-img-preview .wip-item:first-child:nth-last-child(2) ~ div { width: 50%; padding-bottom: 40%; } .wall-img-preview .wip-item:first-child:nth-last-child(3), .wall-img-preview .wip-item:first-child:nth-last-child(3) ~ div, .wall-img-preview .wip-item:first-child:nth-last-child(4), .wall-img-preview .wip-item:first-child:nth-last-child(4) ~ div:not(:last-child), .wall-img-preview .wip-item:first-child:nth-last-child(5), .wall-img-preview .wip-item:first-child:nth-last-child(5) ~ div:not( :nth-last-of-type(-n+2)), .wall-img-preview .wip-item:first-child:nth-last-child(6), .wall-img-preview .wip-item:first-child:nth-last-child(6) ~ div, .wall-img-preview .wip-item:first-child:nth-last-child(7) ~ div:nth-last-of-type(-n+3) { width: 33.333333%; padding-bottom: 30%; } .wall-img-preview .wip-item:first-child:nth-last-child(5) ~ div:nth-last-of-type(-n+2) { width: 50%; padding-bottom: 40%; } .wall-img-preview .wip-item:first-child:nth-last-child(7), .wall-img-preview .wip-item:first-child:nth-last-child(7) ~ div:not( :nth-last-of-type(-n+3)), .wall-img-preview .wip-item:first-child:nth-last-child(n+8), .wall-img-preview .wip-item:first-child:nth-last-child(n+8) ~ div { width: 25%; padding-bottom: 22%; } .wall-img-preview .wip-item:only-child, .wall-img-preview .wip-item:first-child:nth-last-child(4) ~ div:nth-child(4) { width: 100%; padding-bottom: 50%; } /* * For header type 1 only * You may remove these if you opt header 2 */ #header .skin-switch { padding: 10px 0 2px; text-align: center; } #header .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; display: inline-block; margin: 2px 3px; } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For header type 2 only * You may remove these if you opt header 1 */ #header-2 .skin-switch { position: absolute; right: 50px; bottom: 23px; z-index: 1; } #header-2 .skin-switch .btn { background: #fff; width: 50px; height: 50px; border-radius: 50%; font-size: 25px; z-index: 2; } #header-2 .skin-switch .dropdown-menu { min-width: 130px; height: 130px; border-radius: 50%; width: 130px; top: -42px; left: -40px; z-index: 1; -webkit-transform-origin: center; -moz-transform-origin: center; -ms-transform-origin: center; transform-origin: center; -webkit-transform: scale(0) rotate(-360deg); -ms-transform: scale(0) rotate(-360deg); -o-transform: scale(0) rotate(-360deg); transform: scale(0) rotate(-360deg); -webkit-transition-duration: 500ms; transition-duration: 500ms; } #header-2 .skin-switch .dropdown-menu .ss-skin { position: absolute; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-1 { margin-left: -8px; top: 12px; left: 50%; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-2 { right: 24px; top: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-3 { top: 50%; margin-top: -8px; right: 12px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-4 { right: 24px; bottom: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-5 { margin-left: -8px; bottom: 12px; left: 50%; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-6 { left: 24px; bottom: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-7 { top: 50%; margin-top: -8px; left: 12px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-8 { left: 24px; top: 26px; } #header-2 .skin-switch.open .dropdown-menu { -webkit-transform: scale(1) rotate(0deg); -ms-transform: scale(1) rotate(0deg); -o-transform: scale(1) rotate(0deg); transform: scale(1) rotate(0deg); } /* ----------------------------- End header type 2 ------------------------------------- */ /* * Do not remove these * This is common for both */ .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; } .ss-skin:hover { opacity: 0.8; filter: alpha(opacity=80); } [data-current-skin="lightblue"] { background-color: #03a9f4; } [data-current-skin="lightblue"] .ss-icon { color: #03a9f4; } @media (max-width: 767px) { [data-current-skin="lightblue"] .ha-menu { background: #03a9f4; } } [data-current-skin="bluegray"] { background-color: #607d8b; } [data-current-skin="bluegray"] .ss-icon { color: #607d8b; } @media (max-width: 767px) { [data-current-skin="bluegray"] .ha-menu { background: #607d8b; } } [data-current-skin="blue"] { background-color: #2196f3; } [data-current-skin="blue"] .ss-icon { color: #2196f3; } @media (max-width: 767px) { [data-current-skin="blue"] .ha-menu { background: #2196f3; } } [data-current-skin="purple"] { background-color: #9c27b0; } [data-current-skin="purple"] .ss-icon { color: #9c27b0; } @media (max-width: 767px) { [data-current-skin="purple"] .ha-menu { background: #9c27b0; } } [data-current-skin="orange"] { background-color: #ff9800; } [data-current-skin="orange"] .ss-icon { color: #ff9800; } @media (max-width: 767px) { [data-current-skin="orange"] .ha-menu { background: #ff9800; } } [data-current-skin="cyan"] { background-color: #00bcd4; } [data-current-skin="cyan"] .ss-icon { color: #00bcd4; } @media (max-width: 767px) { [data-current-skin="cyan"] .ha-menu { background: #00bcd4; } } [data-current-skin="green"] { background-color: #4caf50; } [data-current-skin="green"] .ss-icon { color: #4caf50; } @media (max-width: 767px) { [data-current-skin="green"] .ha-menu { background: #4caf50; } } [data-current-skin="teal"] { background-color: #009688; } [data-current-skin="teal"] .ss-icon { color: #009688; } @media (max-width: 767px) { [data-current-skin="teal"] .ha-menu { background: #009688; } } [data-current-skin="pink"] { background-color: #e91e63; } [data-current-skin="pink"] .ss-icon { color: #e91e63; } @media (max-width: 767px) { [data-current-skin="pink"] .ha-menu { background: #e91e63; } } .preloader { position: relative; margin: 0px auto; display: inline-block; } .preloader:not([class*="pl-"]) { width: 40px; } .preloader:before { content: ''; display: block; padding-top: 100%; } .preloader.pl-xs { width: 20px; } .preloader.pl-sm { width: 30px; } .preloader.pl-lg { width: 50px; } .preloader.pl-xl { width: 80px; } .preloader.pl-xxl { width: 100px; } .preloader:not([class*="pls-"]) .plc-path { animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite; } .preloader[class*="pls-"] .plc-path { animation: dash 1.5s ease-in-out infinite; } .preloader.pls-red .plc-path { stroke: #f44336; } .preloader.pls-blue .plc-path { stroke: #2196f3; } .preloader.pls-green .plc-path { stroke: #4caf50; } .preloader.pls-yellow .plc-path { stroke: #ffeb3b; } .preloader.pls-bluegray .plc-path { stroke: #607d8b; } .preloader.pls-amber .plc-path { stroke: #ffc107; } .preloader.pls-teal .plc-path { stroke: #009688; } .preloader.pls-gray .plc-path { stroke: #9e9e9e; } .preloader.pls-pink .plc-path { stroke: #e91e63; } .preloader.pls-purple .plc-path { stroke: #9c27b0; } .preloader.pls-white .plc-path { stroke: #fff; } .pl-circular { animation: rotate 2s linear infinite; height: 100%; transform-origin: center center; width: 100%; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; } .plc-path { stroke-dasharray: 1,200; stroke-dashoffset: 0; stroke-linecap: round; stroke-width: 2; stroke-miterlimit: 10; fill: none; } @keyframes rotate { 100% { transform: rotate(360deg); } } @keyframes dash { 0% { stroke-dasharray: 1,200; stroke-dashoffset: 0; } 50% { stroke-dasharray: 89,200; stroke-dashoffset: -35px; } 100% { stroke-dasharray: 89,200; stroke-dashoffset: -124px; } } @keyframes color { 100%, 0% { stroke: #f44336; } 40% { stroke: #2196f3; } 66% { stroke: #4caf50; } 80%, 90% { stroke: #ffc107; } } @media print { @page { margin: 0; size: auto; } body { margin: 0mm 0mm 0mm 0mm !important; padding: 0mm !important; } #header, #footer, #sidebar, #chat, .growl-animated, .m-btn { display: none !important; } /* * INVOICE */ .invoice { padding: 30px !important; -webkit-print-color-adjust: exact !important; } .invoice .card-header { background: #eee !important; padding: 20px; margin-bottom: 20px; margin: -60px -30px 25px -30px; } .invoice .block-header { display: none; } .invoice .highlight { background: #eee !important; } } /* * Vendor Overrides */ .mejs-container { outline: none; } .mejs-container .mejs-controls { background: #ec592f; height: 50px; padding: 10px 5px 0; } .mejs-container .mejs-controls div { height: 5px; } .mejs-container .mejs-controls div.mejs-time-rail { position: absolute; left: 0; top: 0; padding: 0; width: 100% !important; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-total { margin: 0; width: 100% !important; background: #ec592f; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-loaded { background: #D04B25; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-current { background: #ffea00; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-buffering { background: #ec592f; } .mejs-container .mejs-controls div.mejs-time-rail span:not(.mejs-time-float), .mejs-container .mejs-controls div.mejs-time-rail a { border-radius: 0; height: 3px; } .mejs-container .mejs-controls .mejs-button button { background-color: #ec592f; width: 15px; height: 15px; background-position: center; } .mejs-container .mejs-controls .mejs-button button:focus { outline: none !important; } .mejs-container .mejs-controls .mejs-volume-button { position: absolute; right: 35px; } .mejs-container .mejs-controls .mejs-play button { background-image: url("../img/icons/play.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-play button { background-image: url("../img/icons/play@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-pause button { background-image: url("../img/icons/pause.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-pause button { background-image: url("../img/icons/pause@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-mute button { background-image: url("../img/icons/speaker.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-mute button { background-image: url("../img/icons/speaker@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-unmute button { background-image: url("../img/icons/speaker-2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-unmute button { background-image: url("../img/icons/speaker-2@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-fullscreen-button { position: absolute; right: 5px; } .mejs-container .mejs-controls .mejs-fullscreen-button button { background-image: url("../img/icons/fullscreen.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-fullscreen-button button { background-image: url("../img/icons/fullscreen@2x.png"); background-size: 15px 15px; } } /** CALENDAR WIDGET **/ #calendar-widget { margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } #fc-actions { position: absolute; bottom: 10px; right: 12px; } .fc { background-color: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); margin-bottom: 30px; } .fc td, .fc th { border-color: #f0f0f0; } .fc th { font-weight: 400; } .fc table { background: transparent; } .fc table tr > td:first-child { border-left-width: 0; } #calendar-widget .fc-toolbar { background: #009688; } #calendar-widget .fc-day-header { color: #fff; background: #007d71; padding: 5px 0; border-width: 0; } #calendar-widget .fc-day-number { text-align: center; color: #ADADAD; padding: 5px 0; } #calendar-widget .fc-day-grid-event { margin: 1px 3px 1px; } #calendar-widget .ui-widget-header th, #calendar-widget .ui-widget-header { border-width: 0; } #calendar .fc-toolbar { height: 300px; background-image: url('../img/cal-header.jpg'); background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; background-position: inherit; } #calendar .fc-toolbar:before { content: ""; height: 50px; width: 100%; background: rgba(0, 0, 0, 0.36); position: absolute; bottom: 0; left: 0; } #calendar .fc-toolbar .fc-center { margin-top: 238px; position: relative; } @media screen and (max-width: 991px) { #calendar .fc-toolbar { height: 200px; } #calendar .fc-toolbar .fc-center { margin-top: 138px; } } #calendar .fc-day-header { color: #ADADAD; text-align: left; font-size: 14px; border-bottom-width: 0; border-right-color: #eee; padding: 10px 12px; } #calendar .fc-day-number { padding-left: 10px !important; color: #CCC; text-align: left !important; } @media screen and (min-width: 991px) { #calendar .fc-day-number { font-size: 25px; letter-spacing: -2px; } } #calendar .fc-day-grid-event { margin: 1px 9px 0; } .fc-today { color: #ffc107; } .fc-toolbar { margin-bottom: 0; padding: 20px 7px 19px; position: relative; } .fc-toolbar h2 { margin-top: 7px; font-size: 20px; font-weight: 400; text-transform: uppercase; color: #fff; } .fc-toolbar .ui-button { border: 0; background: 0 0; padding: 0; outline: none !important; text-align: center; width: 30px; height: 30px; border-radius: 50%; margin-top: 2px; color: #fff; } .fc-toolbar .ui-button:hover { background: #fff; color: #009688; } .fc-toolbar .ui-button > span { position: relative; font-family: 'Material-Design-Iconic-Font'; font-size: 20px; line-height: 100%; width: 30px; display: block; margin-top: 2px; } .fc-toolbar .ui-button > span:before { position: relative; z-index: 1; } .fc-toolbar .ui-button > span.ui-icon-circle-triangle-w:before { content: "\f2fa"; } .fc-toolbar .ui-button > span.ui-icon-circle-triangle-e:before { content: "\f2fb"; } .fc-event { padding: 0; font-size: 11px; border-radius: 0; border: 0; } .fc-event .fc-title { padding: 2px 8px; display: block; } .fc-event .fc-time { float: left; background: rgba(0, 0, 0, 0.2); padding: 2px 6px; margin: 0 0 0 -1px; } .fc-view, .fc-view > table { border: 0; overflow: hidden; } .fc-view > table > tbody > tr > .ui-widget-content { border-top: 0; } div.fc-row { margin-right: 0 !important; border: 0 !important; } .fc-today { color: #ffc107 !important; } /* Even Tag Color */ .event-tag { margin-top: 5px; } .event-tag > span { border-radius: 50%; width: 30px; height: 30px; margin-right: 3px; position: relative; display: inline-block; cursor: pointer; } .event-tag > span:hover { opacity: 0.8; filter: alpha(opacity=80); } .event-tag > span.selected:before { font-family: 'Material-Design-Iconic-Font'; content: "\f26b"; position: absolute; text-align: center; top: 3px; width: 100%; font-size: 17px; color: #FFF; } hr.fc-divider { border-width: 1px; border-color: #eee; } .fc-day-grid-container.fc-scroller { height: auto !important; overflow: hidden !important; } .bootgrid-footer .infoBar, .bootgrid-header .actionBar { text-align: left; } .bootgrid-footer .search, .bootgrid-header .search { vertical-align: top; } .bootgrid-header { padding: 0 25px 10px; } .bootgrid-header .search { border: 1px solid #e0e0e0; } .bootgrid-header .search .form-control, .bootgrid-header .search .input-group-addon { border: 0; } .bootgrid-header .search .glyphicon-search { vertical-align: top; padding: 9px 10px 0; } .bootgrid-header .search .glyphicon-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; font-size: 17px; vertical-align: top; line-height: 100%; } @media (min-width: 480px) { .bootgrid-header .search { width: 300px; } } @media (max-width: 480px) { .bootgrid-header .search { width: 100%; padding-right: 90px; } } .bootgrid-header .actions { box-shadow: none; } .bootgrid-header .actions .btn-group { border: 1px solid #e0e0e0; } .bootgrid-header .actions .btn-group .btn { height: 35px; box-shadow: none !important; background: transparent; } .bootgrid-header .actions .btn-group .dropdown-menu { padding: 10px 20px; } .bootgrid-header .actions .btn-group .dropdown-menu .dropdown-item { padding: 0 0 0 27px !important; } .bootgrid-header .actions .btn-group .dropdown-menu .dropdown-item:hover { background-color: #fff !important; } @media (min-width: 768px) { .bootgrid-header .actions .btn-group .dropdown-menu { left: 0; -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; margin-top: 1px; } } .bootgrid-header .actions .btn-group .caret { display: none; } .bootgrid-header .actions .btn-group .zmdi { line-height: 100%; font-size: 18px; vertical-align: top; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .bootgrid-header .actions .btn-group.open .zmdi { -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg); } @media (max-width: 480px) { .bootgrid-header .actions { position: absolute; top: 0; right: 15px; } } .bootgrid-table th > .column-header-anchor > .icon { top: 0px; font-size: 20px; line-height: 100%; } .bootgrid-footer .col-sm-6 { padding: 10px 30px 20px; } @media (max-width: 768px) { .bootgrid-footer .col-sm-6 { text-align: center; } } @media (max-width: 768px) { .bootgrid-footer .infoBar { display: none; } } .bootgrid-footer .infoBar .infos { border: 1px solid #EEE; display: inline-block; float: right; padding: 7px 30px; font-size: 12px; margin-top: 5px; } .select-cell .checkbox { margin: 0; } .command-edit, .command-delete { background: #fff; } .bootstrap-select .dropdown-menu { padding: 0; } .bootstrap-select .dropdown-toggle:focus { outline: none !important; } .bootstrap-select > .btn-default { background: none !important; border-bottom: 1px solid #e0e0e0 !important; border-radius: 0; padding-left: 0; padding-right: 0; } .bootstrap-select > .btn-default:before { position: absolute; top: 0; right: 0; content: ""; height: calc(100% - 2px); width: 30px; background-color: #FFF; background-position: right calc(100% - 7px); background-repeat: no-repeat; background-image: url("../img/select.png"); pointer-events: none; z-index: 5; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .bootstrap-select > .btn-default:before { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } .bootstrap-select > .btn-default:after { position: absolute; z-index: 3; bottom: -1px; left: 0; height: 2px; width: 0; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .bootstrap-select > .btn-default:not(.disabled):after, .bootstrap-select > .btn-default:not(.readonly):after { background: #2196f3; } .bootstrap-select > .btn-default.disabled:after, .bootstrap-select > .btn-default.readonly:after { background: #ccc; } .bootstrap-select.open > .btn-default:after { width: 100%; } .bootstrap-select .bs-searchbox { padding: 5px 5px 5px 40px; position: relative; background: #f7f7f7; } .bootstrap-select .bs-searchbox:before { position: absolute; left: 0; top: 0; width: 40px; height: 100%; content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; font-size: 25px; padding: 4px 0 0 15px; } .bootstrap-select .bs-searchbox input { border: 0; background: transparent; } .bootstrap-select.btn-group .dropdown-menu li a.opt { padding-left: 17px; } .bootstrap-select .check-mark { margin-top: -5px !important; font-size: 19px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); display: block !important; position: absolute; top: 11px; right: 15px; } .bootstrap-select .check-mark:before { content: "\f26b"; font-family: 'Material-Design-Iconic-Font'; } .bootstrap-select .selected .check-mark { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .bootstrap-select .notify { bottom: 0 !important; margin: 0 !important; width: 100% !important; border: 0 !important; background: #f44336 !important; color: #fff !important; text-align: center; } .bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) { width: 100%; } .chosen-container .chosen-drop { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); margin-top: 1px; border: 0; left: 0; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; -webkit-transition: transform opacity; -o-transition: transform opacity; transition: transform opacity; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .chosen-container.chosen-with-drop .chosen-drop { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .chosen-container.chosen-with-drop .chosen-single:after { width: 100%; } .chosen-container .chosen-results { margin: 0; padding: 0; max-height: 300px; } .chosen-container .chosen-results li { padding: 10px 17px; width: 100%; } .chosen-container .chosen-results li.highlighted { background: rgba(0, 0, 0, 0.075); color: #333333; } .chosen-container .chosen-results li.result-selected { background: transparent; color: #5e5e5e; position: relative; } .chosen-container .chosen-results li.result-selected:before { content: "\f26b"; font-family: 'Material-Design-Iconic-Font'; position: absolute; right: 15px; top: 10px; font-size: 19px; } .chosen-container .chosen-results li.group-result { color: #B2B2B2; font-weight: normal; padding: 16px 15px 6px; margin-top: 9px; } .chosen-container .chosen-results li.group-result:not(:first-child) { border-top: 1px solid #eee; } .chosen-container-single .chosen-single { border-radius: 0; overflow: visible; height: 34px; padding: 6px 0 6px; text-transform: uppercase; border: 0; border-bottom: 1px solid #e0e0e0; background: none; box-shadow: none; } .chosen-container-single .chosen-single:after { content: ""; width: 0; background: #2196f3; height: 2px; position: absolute; left: 0; bottom: -1px; -webkit-transition: width; -o-transition: width; transition: width; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .chosen-container-single .chosen-single div b { background-image: url("../img/select.png"); background-repeat: no-repeat; background-position: right 12px; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .chosen-container-single .chosen-single div b { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } .chosen-container-single .chosen-search { padding: 5px 5px 5px 40px; background: #f7f7f7; } .chosen-container-single .chosen-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 0; top: 0; width: 40px; height: 100%; font-size: 25px; padding: 5px 0 0 15px; } .chosen-container-single .chosen-search input[type=text] { border: 0; height: 35px; line-height: 1.42857143; background: none; } .chosen-container-active.chosen-with-drop .chosen-single { border: 0; background: none; } .chosen-container-multi .chosen-choices { padding: 0; border: 0; border-bottom: 1px solid #e0e0e0; background: none; box-shadow: none; } .chosen-container-multi .chosen-choices li.search-choice { border-radius: 2px; margin: 4px 4px 0 0; background: #eaeaea; padding: 5px 23px 5px 8px; border: 0; box-shadow: none; } .chosen-container-multi .chosen-choices li.search-choice .search-choice-close { background-image: none; } .chosen-container-multi .chosen-choices li.search-choice .search-choice-close:before { display: inline-block; font-family: 'Material-Design-Iconic-Font'; content: "\f135"; position: relative; top: 1px; color: #9C9C9C; z-index: 2; font-size: 12px; } .chosen-container-multi .chosen-choices li.search-field input[type=text] { padding: 0; height: 31px; } select.chosen { display: none; } .noUi-target { border-radius: 0; box-shadow: none; border: 0; } .noUi-background { background: #d4d4d4; box-shadow: none; } .noUi-horizontal { height: 3px; } .noUi-horizontal .noUi-handle { top: -8px; } .noUi-vertical { width: 3px; } .noUi-horizontal .noUi-handle, .noUi-vertical .noUi-handle { width: 19px; height: 19px; border: 0; border-radius: 100%; box-shadow: none; -webkit-transition: box-shadow; -o-transition: box-shadow; transition: box-shadow; -webkit-transition-duration: 200ms; transition-duration: 200ms; cursor: pointer; position: relative; } .noUi-horizontal .noUi-handle:before, .noUi-vertical .noUi-handle:before, .noUi-horizontal .noUi-handle:after, .noUi-vertical .noUi-handle:after { display: none; } .noUi-horizontal .noUi-handle:active, .noUi-vertical .noUi-handle:active { background: #ccc !important; } .noUi-horizontal .noUi-handle .is-tooltip, .noUi-vertical .noUi-handle .is-tooltip { position: absolute; bottom: 32px; height: 35px; border-radius: 2px; color: #fff; text-align: center; line-height: 33px; width: 50px; left: 50%; margin-left: -25px; padding: 0 10px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; opacity: 0; filter: alpha(opacity=0); -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .noUi-horizontal .noUi-handle .is-tooltip:after, .noUi-vertical .noUi-handle .is-tooltip:after { width: 0; height: 0; border-style: solid; border-width: 15px 10px 0 10px; position: absolute; bottom: -8px; left: 50%; margin-left: -9px; content: ""; } .noUi-horizontal .noUi-active, .noUi-vertical .noUi-active { box-shadow: 0 0 0 13px rgba(0, 0, 0, 0.1); } .noUi-horizontal .noUi-active .is-tooltip, .noUi-vertical .noUi-active .is-tooltip { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); bottom: 40px; opacity: 1; filter: alpha(opacity=100); } .input-slider:not([data-is-color]) .noUi-handle, .input-slider-range:not([data-is-color]) .noUi-handle, .input-slider-values:not([data-is-color]) .noUi-handle, .input-slider:not([data-is-color]) .noUi-connect, .input-slider-range:not([data-is-color]) .noUi-connect, .input-slider-values:not([data-is-color]) .noUi-connect { background: #009688 !important; } .input-slider:not([data-is-color]) .is-tooltip, .input-slider-range:not([data-is-color]) .is-tooltip, .input-slider-values:not([data-is-color]) .is-tooltip { background: #009688; } .input-slider:not([data-is-color]) .is-tooltip:after, .input-slider-range:not([data-is-color]) .is-tooltip:after, .input-slider-values:not([data-is-color]) .is-tooltip:after { border-color: #009688 transparent transparent transparent; } .input-slider[data-is-color=red] .noUi-handle, .input-slider-range[data-is-color=red] .noUi-handle, .input-slider-values[data-is-color=red] .noUi-handle, .input-slider[data-is-color=red] .noUi-connect, .input-slider-range[data-is-color=red] .noUi-connect, .input-slider-values[data-is-color=red] .noUi-connect { background: #f44336 !important; } .input-slider[data-is-color=blue] .noUi-handle, .input-slider-range[data-is-color=blue] .noUi-handle, .input-slider-values[data-is-color=blue] .noUi-handle, .input-slider[data-is-color=blue] .noUi-connect, .input-slider-range[data-is-color=blue] .noUi-connect, .input-slider-values[data-is-color=blue] .noUi-connect { background: #2196f3 !important; } .input-slider[data-is-color=cyan] .noUi-handle, .input-slider-range[data-is-color=cyan] .noUi-handle, .input-slider-values[data-is-color=cyan] .noUi-handle, .input-slider[data-is-color=cyan] .noUi-connect, .input-slider-range[data-is-color=cyan] .noUi-connect, .input-slider-values[data-is-color=cyan] .noUi-connect { background: #00bcd4 !important; } .input-slider[data-is-color=amber] .noUi-handle, .input-slider-range[data-is-color=amber] .noUi-handle, .input-slider-values[data-is-color=amber] .noUi-handle, .input-slider[data-is-color=amber] .noUi-connect, .input-slider-range[data-is-color=amber] .noUi-connect, .input-slider-values[data-is-color=amber] .noUi-connect { background: #ffc107 !important; } .input-slider[data-is-color=green] .noUi-handle, .input-slider-range[data-is-color=green] .noUi-handle, .input-slider-values[data-is-color=green] .noUi-handle, .input-slider[data-is-color=green] .noUi-connect, .input-slider-range[data-is-color=green] .noUi-connect, .input-slider-values[data-is-color=green] .noUi-connect { background: #4caf50 !important; } .input-slider .noUi-origin { background: #d4d4d4; } .input-slider:not([data-is-color]) .noUi-base { background: #009688 !important; } .input-slider[data-is-color=red] .noUi-base { background: #f44336 !important; } .input-slider[data-is-color=blue] .noUi-base { background: #2196f3 !important; } .input-slider[data-is-color=cyan] .noUi-base { background: #00bcd4 !important; } .input-slider[data-is-color=amber] .noUi-base { background: #ffc107 !important; } .input-slider[data-is-color=green] .noUi-base { background: #4caf50 !important; } .cp-container { position: relative; } .cp-container > .input-group input.cp-value { color: #000 !important; background: transparent !important; } .cp-container > .input-group .dropdown-menu { padding: 20px; margin-left: 10px; } .cp-container i.cp-value { width: 25px; height: 25px; border-radius: 2px; position: absolute; top: 0; right: 15px; } .note-editor .note-toolbar, .note-popover .note-toolbar, .note-editor .popover-content, .note-popover .popover-content { background: #fff; border-color: #e4e4e4; margin: 0; padding: 10px 0 15px; text-align: center; } .note-editor .note-toolbar > .btn-group, .note-popover .note-toolbar > .btn-group, .note-editor .popover-content > .btn-group, .note-popover .popover-content > .btn-group { display: inline-block; float: none; box-shadow: none; } .note-editor .note-toolbar > .btn-group .btn, .note-popover .note-toolbar > .btn-group .btn, .note-editor .popover-content > .btn-group .btn, .note-popover .popover-content > .btn-group .btn { margin: 0 1px; } .note-editor .note-toolbar > .btn-group > .active, .note-popover .note-toolbar > .btn-group > .active, .note-editor .popover-content > .btn-group > .active, .note-popover .popover-content > .btn-group > .active { background: #00bcd4; color: #fff; } .note-editor .note-toolbar .btn, .note-popover .note-toolbar .btn, .note-editor .popover-content .btn, .note-popover .popover-content .btn { height: 40px; border-radius: 2px !important; box-shadow: none !important; } .note-editor .note-toolbar .btn:active, .note-popover .note-toolbar .btn:active, .note-editor .popover-content .btn:active, .note-popover .popover-content .btn:active { box-shadow: none; } .note-editor .note-toolbar .note-palette-title, .note-popover .note-toolbar .note-palette-title, .note-editor .popover-content .note-palette-title, .note-popover .popover-content .note-palette-title { margin: 0 !important; padding: 10px 0 !important; font-size: 13px !important; text-align: center !important; border: 0 !important; } .note-editor .note-toolbar .note-color-reset, .note-popover .note-toolbar .note-color-reset, .note-editor .popover-content .note-color-reset, .note-popover .popover-content .note-color-reset { padding: 0 0 10px !important; margin: 0 !important; background: none; text-align: center; } .note-editor .note-toolbar .note-color .dropdown-menu, .note-popover .note-toolbar .note-color .dropdown-menu, .note-editor .popover-content .note-color .dropdown-menu, .note-popover .popover-content .note-color .dropdown-menu { min-width: 335px; } .note-editor .note-statusbar .note-resizebar, .note-popover .note-statusbar .note-resizebar { border-color: #E8E8E8; } .note-editor .note-statusbar .note-resizebar .note-icon-bar, .note-popover .note-statusbar .note-resizebar .note-icon-bar { border-color: #BCBCBC; } .note-editor .fa, .note-popover .fa { font-style: normal; font-size: 20px; vertical-align: middle; } .note-editor .fa:before, .note-popover .fa:before { font-family: 'Material-Design-Iconic-Font'; } .note-editor .fa.fa-magic:before, .note-popover .fa.fa-magic:before { content: "\f16a"; } .note-editor .fa.fa-bold:before, .note-popover .fa.fa-bold:before { content: "\f23d"; } .note-editor .fa.fa-italic:before, .note-popover .fa.fa-italic:before { content: "\f245"; } .note-editor .fa.fa-underline:before, .note-popover .fa.fa-underline:before { content: "\f24f"; } .note-editor .fa.fa-font:before, .note-popover .fa.fa-font:before { content: "\f242"; } .note-editor .fa.fa-list-ul:before, .note-popover .fa.fa-list-ul:before { content: "\f247"; } .note-editor .fa.fa-list-ol:before, .note-popover .fa.fa-list-ol:before { content: "\f248"; } .note-editor .fa.fa-align-left:before, .note-popover .fa.fa-align-left:before { content: "\f23b"; } .note-editor .fa.fa-align-right:before, .note-popover .fa.fa-align-right:before { content: "\f23c"; } .note-editor .fa.fa-align-center:before, .note-popover .fa.fa-align-center:before { content: "\f239"; } .note-editor .fa.fa-align-justify:before, .note-popover .fa.fa-align-justify:before { content: "\f23a"; } .note-editor .fa.fa-indent:before, .note-popover .fa.fa-indent:before { content: "\f244"; } .note-editor .fa.fa-outdent:before, .note-popover .fa.fa-outdent:before { content: "\f243"; } .note-editor .fa.fa-text-height:before, .note-popover .fa.fa-text-height:before { content: "\f246"; } .note-editor .fa.fa-table:before, .note-popover .fa.fa-table:before { content: "\f320"; } .note-editor .fa.fa-link:before, .note-popover .fa.fa-link:before { content: "\f18e"; } .note-editor .fa.fa-picture-o:before, .note-popover .fa.fa-picture-o:before { content: "\f17f"; } .note-editor .fa.fa-minus:before, .note-popover .fa.fa-minus:before { content: "\f22f"; } .note-editor .fa.fa-arrows-alt:before, .note-popover .fa.fa-arrows-alt:before { content: "\f16d"; } .note-editor .fa.fa-code:before, .note-popover .fa.fa-code:before { content: "\f13a"; } .note-editor .fa.fa-question:before, .note-popover .fa.fa-question:before { content: "\f1f5"; } .note-editor .fa.fa-eraser:before, .note-popover .fa.fa-eraser:before { content: "\f23f"; } .note-editor .fa.fa-square:before, .note-popover .fa.fa-square:before { content: "\f279"; } .note-editor .fa.fa-circle-o:before, .note-popover .fa.fa-circle-o:before { content: "\f26c"; } .note-editor .fa.fa-times:before, .note-popover .fa.fa-times:before { content: "\f136"; } .note-editor .note-air-popover .arrow, .note-popover .note-air-popover .arrow { left: 20px; } .note-editor { overflow: visible; border: 1px solid #e4e4e4; } .note-editor .note-editable { padding: 20px 23px; } .bootstrap-datetimepicker-widget { padding: 0 !important; margin: 0 !important; width: auto !important; } .bootstrap-datetimepicker-widget:after, .bootstrap-datetimepicker-widget:before { display: none !important; } .bootstrap-datetimepicker-widget table td { text-shadow: none; } .bootstrap-datetimepicker-widget table td span { margin: 0; } .bootstrap-datetimepicker-widget table td span:hover { background: transparent; } .bootstrap-datetimepicker-widget .glyphicon { font-family: 'Material-Design-Iconic-Font'; font-size: 18px; } .bootstrap-datetimepicker-widget .glyphicon-chevron-left:before { content: "\f2ff"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-right:before { content: "\f301"; } .bootstrap-datetimepicker-widget .glyphicon-time:before { content: "\f337"; } .bootstrap-datetimepicker-widget .glyphicon-calendar:before { content: "\f32e"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-up:before { content: "\f1e5"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-down:before { content: "\f1e4"; } .bootstrap-datetimepicker-widget [data-action="togglePicker"] span { font-size: 25px; color: #ccc; } .bootstrap-datetimepicker-widget [data-action="togglePicker"] span:hover { color: #333; } .bootstrap-datetimepicker-widget a[data-action] { color: #009688; } .timepicker-picker .btn { box-shadow: none !important; } .timepicker-picker table tbody tr + tr:not(:last-child) { background: #009688; color: #fff; } .timepicker-picker table tbody tr + tr:not(:last-child) td { border-radius: 0; } .timepicker-picker .btn, .timepicker-picker .btn:hover { background: #fff; color: #333; } .datepicker.top { -webkit-transform-origin: 0 100% !important; -moz-transform-origin: 0 100% !important; -ms-transform-origin: 0 100% !important; transform-origin: 0 100% !important; } .datepicker table thead tr th { border-radius: 0; color: #fff; } .datepicker table thead tr th .glyphicon { width: 30px; height: 30px; border-radius: 50%; line-height: 29px; } .datepicker table thead tr th:hover .glyphicon { background: rgba(0, 0, 0, 0.2); } .datepicker table thead tr:first-child th { background: #009688; padding: 20px 0; } .datepicker table thead tr:first-child th:hover { background: #009688; } .datepicker table thead tr:first-child th.picker-switch { font-size: 16px; font-weight: 400; text-transform: uppercase; } .datepicker table thead tr:last-child th { text-transform: uppercase; font-weight: normal; font-size: 11px; } .datepicker table thead tr:last-child th:first-child { padding-left: 20px; } .datepicker table thead tr:last-child th:last-child { padding-right: 20px; } .datepicker table thead tr:last-child:not(:only-child) { background: #00877a; } .datepicker table tbody tr:last-child td { padding-bottom: 25px; } .datepicker table tbody tr td:first-child { padding-left: 13px; } .datepicker table tbody tr td:last-child { padding-right: 13px; } .datepicker table td.day { width: 35px; height: 35px; line-height: 20px; color: #333; position: relative; padding: 0; background: transparent; } .datepicker table td.day:hover { background: none; } .datepicker table td.day:before { content: ""; width: 35px; height: 35px; border-radius: 50%; margin-bottom: -33px; display: inline-block; background: transparent; position: static; text-shadow: none; } .datepicker table td.day.old, .datepicker table td.day.new { color: #CDCDCD; } .datepicker table td:not(.today):not(.active):hover:before { background: #F0F0F0; } .datepicker table td.today { color: #333; } .datepicker table td.today:before { background-color: #E2E2E2; } .datepicker table td.active { color: #fff; } .datepicker table td.active:before { background-color: #009688; } .datepicker-months .month, .datepicker-years .year, .timepicker-minutes .minute, .timepicker-hours .hour { border-radius: 50%; } .datepicker-months .month:not(.active):hover, .datepicker-years .year:not(.active):hover, .timepicker-minutes .minute:not(.active):hover, .timepicker-hours .hour:not(.active):hover { background: #F0F0F0; } .datepicker-months .month.active, .datepicker-years .year.active, .timepicker-minutes .minute.active, .timepicker-hours .hour.active { background: #009688; } .timepicker-minutes .minute, .timepicker-hours .hour { padding: 0; } .fileinput { position: relative; padding-right: 35px; } .fileinput .close { position: absolute; top: 5px; font-size: 12px; float: none; opacity: 1; font-weight: 500; border: 1px solid #ccc; width: 19px; text-align: center; height: 19px; line-height: 15px; border-radius: 50%; right: 0; } .fileinput .close:hover { background: #eee; } .fileinput .input-group-addon { padding: 0 10px; vertical-align: middle; } .fileinput .fileinput-preview { width: 200px; height: 150px; position: relative; } .fileinput .fileinput-preview img { display: inline-block; vertical-align: middle; margin-top: -13px; } .fileinput .fileinput-preview:after { content: ""; display: inline-block; vertical-align: middle; } #lg-slider:after { content: ""; -webkit-animation-fill-mode: both; animation-fill-mode: both; height: 50px; width: 50px; border-radius: 100%; border: 2px solid #2196f3; -webkit-animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -25px; z-index: -1; } #lg-outer { background: rgba(255, 255, 255, 0.95); } #lg-outer .object { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); border-radius: 2px; } #lg-close { display: none; } #lg-action { top: 0; width: 100%; left: 0; margin-left: 0 !important; height: 40px; text-align: center; } #lg-action > a { background: transparent; color: #9D9D9D; font-size: 18px; width: 28px; height: 37px; } #lg-action > a:hover { background: transparent; color: #000; } #lg-action .cl-thumb { position: fixed; right: 20px; bottom: 20px; width: 50px; height: 50px; border-radius: 50%; line-height: 38px; background: #f44336; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } #lg-action .cl-thumb:after { text-align: center; left: 16px !important; bottom: 6px !important; color: #fff; } #lg-action .cl-thumb:hover { background: #f32c1e; } #lg-gallery .thumb-cont { background: #f44336; text-align: center; } #lg-gallery .thumb-cont .thumb-info { background: #f44336; } #lg-gallery .thumb-cont .thumb-info .count { display: none; } #lg-gallery .thumb-cont .thumb-info .close { width: 14px; margin-top: 0; background: none; } #lg-gallery .thumb-cont .thumb-info .close:hover { background: none; } #lg-gallery .thumb-cont .thumb { opacity: 1; filter: alpha(opacity=100); } #lg-gallery .thumb-cont .thumb-inner { display: inline-block; padding: 12px 12px 15px; } .lg-slide { background: none !important; } .lg-slide em { font-style: normal; } .lg-slide em h3 { margin-bottom: 5px; } .lg-slide .video-cont { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } @-webkit-keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } @keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } .sweet-alert { border-radius: 2px; padding: 10px 30px; } .sweet-alert h2 { font-size: 16px; font-weight: 400; position: relative; z-index: 1; } .sweet-alert .lead { font-size: 13px; } .sweet-alert .btn { padding: 6px 12px; font-size: 13px; margin: 20px 2px 0; } .twitter-typeahead { width: 100%; } .twitter-typeahead .tt-menu { min-width: 200px; background: #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); display: block !important; z-index: 2 !important; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; } .twitter-typeahead .tt-menu.tt-open:not(.tt-empty) { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .twitter-typeahead .tt-suggestion { padding: 8px 17px; color: #333; cursor: pointer; } .twitter-typeahead .tt-suggestion:hover, .twitter-typeahead .tt-cursor { background-color: rgba(0, 0, 0, 0.075); } .twitter-typeahead .tt-hint { color: #818181 !important; } .mCSB_scrollTools { width: 5px; } .mCSB_scrollTools .mCSB_dragger_bar { border-radius: 0 !important; } .mCSB_scrollTools.mCSB_scrollTools_horizontal, .mCSB_scrollTools.mCSB_scrollTools_vertical { margin: 0 !important; } .mCSB_scrollTools.mCSB_scrollTools_horizontal { height: 10px; } .mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar { background: rgba(0, 0, 0, 0.4); } .mCS-minimal-dark.mCSB_scrollTools_onDrag .mCSB_dragger .mCSB_dragger_bar { background: rgba(0, 0, 0, 0.5) !important; } .mCSB_inside > .mCSB_container { margin-right: 0; } ================================================ FILE: material-manage/src/main/webapp/static/css/app.min.1.css ================================================ /* * Load Main Bootstrap LESS files */ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { font-size: 2em; margin: 0.67em 0; } mark { background: #ff0; color: #000; } small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { box-sizing: content-box; height: 0; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { color: inherit; font: inherit; margin: 0; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } input[type="search"] { -webkit-appearance: textfield; box-sizing: content-box; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } legend { border: 0; padding: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { *, *:before, *:after { background: transparent !important; color: #000 !important; box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="#"]:after, a[href^="javascript:"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .navbar { display: none; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table td, .table th { background-color: #fff !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .glyphicon-asterisk:before { content: "\002a"; } .glyphicon-plus:before { content: "\002b"; } .glyphicon-euro:before, .glyphicon-eur:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .glyphicon-cd:before { content: "\e201"; } .glyphicon-save-file:before { content: "\e202"; } .glyphicon-open-file:before { content: "\e203"; } .glyphicon-level-up:before { content: "\e204"; } .glyphicon-copy:before { content: "\e205"; } .glyphicon-paste:before { content: "\e206"; } .glyphicon-alert:before { content: "\e209"; } .glyphicon-equalizer:before { content: "\e210"; } .glyphicon-king:before { content: "\e211"; } .glyphicon-queen:before { content: "\e212"; } .glyphicon-pawn:before { content: "\e213"; } .glyphicon-bishop:before { content: "\e214"; } .glyphicon-knight:before { content: "\e215"; } .glyphicon-baby-formula:before { content: "\e216"; } .glyphicon-tent:before { content: "\26fa"; } .glyphicon-blackboard:before { content: "\e218"; } .glyphicon-bed:before { content: "\e219"; } .glyphicon-apple:before { content: "\f8ff"; } .glyphicon-erase:before { content: "\e221"; } .glyphicon-hourglass:before { content: "\231b"; } .glyphicon-lamp:before { content: "\e223"; } .glyphicon-duplicate:before { content: "\e224"; } .glyphicon-piggy-bank:before { content: "\e225"; } .glyphicon-scissors:before { content: "\e226"; } .glyphicon-bitcoin:before { content: "\e227"; } .glyphicon-btc:before { content: "\e227"; } .glyphicon-xbt:before { content: "\e227"; } .glyphicon-yen:before { content: "\00a5"; } .glyphicon-jpy:before { content: "\00a5"; } .glyphicon-ruble:before { content: "\20bd"; } .glyphicon-rub:before { content: "\20bd"; } .glyphicon-scale:before { content: "\e230"; } .glyphicon-ice-lolly:before { content: "\e231"; } .glyphicon-ice-lolly-tasted:before { content: "\e232"; } .glyphicon-education:before { content: "\e233"; } .glyphicon-option-horizontal:before { content: "\e234"; } .glyphicon-option-vertical:before { content: "\e235"; } .glyphicon-menu-hamburger:before { content: "\e236"; } .glyphicon-modal-window:before { content: "\e237"; } .glyphicon-oil:before { content: "\e238"; } .glyphicon-grain:before { content: "\e239"; } .glyphicon-sunglasses:before { content: "\e240"; } .glyphicon-text-size:before { content: "\e241"; } .glyphicon-text-color:before { content: "\e242"; } .glyphicon-text-background:before { content: "\e243"; } .glyphicon-object-align-top:before { content: "\e244"; } .glyphicon-object-align-bottom:before { content: "\e245"; } .glyphicon-object-align-horizontal:before { content: "\e246"; } .glyphicon-object-align-left:before { content: "\e247"; } .glyphicon-object-align-vertical:before { content: "\e248"; } .glyphicon-object-align-right:before { content: "\e249"; } .glyphicon-triangle-right:before { content: "\e250"; } .glyphicon-triangle-left:before { content: "\e251"; } .glyphicon-triangle-bottom:before { content: "\e252"; } .glyphicon-triangle-top:before { content: "\e253"; } .glyphicon-console:before { content: "\e254"; } .glyphicon-superscript:before { content: "\e255"; } .glyphicon-subscript:before { content: "\e256"; } .glyphicon-menu-left:before { content: "\e257"; } .glyphicon-menu-right:before { content: "\e258"; } .glyphicon-menu-down:before { content: "\e259"; } .glyphicon-menu-up:before { content: "\e260"; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: roboto; font-size: 13px; line-height: 1.42857143; color: #5e5e5e; background-color: #edecec; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #2196f3; text-decoration: none; } a:hover, a:focus { color: #0a6ebd; text-decoration: none; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; max-width: 100%; height: auto; } .img-rounded { border-radius: 2px; } .img-thumbnail { padding: 3px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #ededed; border-radius: 2px; -webkit-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; display: inline-block; max-width: 100%; height: auto; } .img-circle { border-radius: 50%; } hr { margin-top: 18px; margin-bottom: 18px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } [role="button"] { cursor: pointer; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: #000000; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #777777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 18px; margin-bottom: 9px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 9px; margin-bottom: 9px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 33px; } h2, .h2 { font-size: 27px; } h3, .h3 { font-size: 23px; } h4, .h4 { font-size: 17px; } h5, .h5 { font-size: 13px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 9px; } .lead { margin-bottom: 18px; font-size: 14px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 19.5px; } } small, .small { font-size: 92%; } mark, .mark { background-color: #ffa829; padding: .2em; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777777; } .text-primary { color: #2196f3; } a.text-primary:hover, a.text-primary:focus { color: #0c7cd5; } .text-success { color: #67bd6a; } a.text-success:hover, a.text-success:focus { color: #49a84d; } .text-info { color: #31708f; } a.text-info:hover, a.text-info:focus { color: #245269; } .text-warning { color: #ffa829; } a.text-warning:hover, a.text-warning:focus { color: #f59200; } .text-danger { color: #f6675d; } a.text-danger:hover, a.text-danger:focus { color: #f33a2c; } .bg-primary { color: #fff; background-color: #2196f3; } a.bg-primary:hover, a.bg-primary:focus { background-color: #0c7cd5; } .bg-success { background-color: #67bd6a; } a.bg-success:hover, a.bg-success:focus { background-color: #49a84d; } .bg-info { background-color: #d9edf7; } a.bg-info:hover, a.bg-info:focus { background-color: #afd9ee; } .bg-warning { background-color: #ffa829; } a.bg-warning:hover, a.bg-warning:focus { background-color: #f59200; } .bg-danger { background-color: #f6675d; } a.bg-danger:hover, a.bg-danger:focus { background-color: #f33a2c; } .page-header { padding-bottom: 8px; margin: 36px 0 18px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 9px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; margin-left: -5px; } .list-inline > li { display: inline-block; padding-left: 5px; padding-right: 5px; } dl { margin-top: 0; margin-bottom: 18px; } dt, dd { line-height: 1.42857143; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; clear: left; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 9px 18px; margin: 0 0 18px; font-size: 16.25px; border-left: 5px solid #eeeeee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; text-align: right; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } address { margin-bottom: 18px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 2px; } kbd { padding: 2px 4px; font-size: 90%; color: #ffffff; background-color: #333333; border-radius: 2px; box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; box-shadow: none; } pre { display: block; padding: 8.5px; margin: 0 0 9px; font-size: 12px; line-height: 1.42857143; word-break: break-all; word-wrap: break-word; color: #333333; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 2px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } @media (min-width: 768px) { .container { width: 100%; } } @media (min-width: 992px) { .container { width: 100%; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } .row { margin-left: -15px; margin-right: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-left: 15px; padding-right: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0%; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0%; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0%; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0%; } } table { background-color: #ffffff; } caption { padding-top: 10px; padding-bottom: 10px; color: #777777; text-align: left; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 18px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 10px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #f0f0f0; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #f0f0f0; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #f0f0f0; } .table .table { background-color: #edecec; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 7px; } .table-bordered { border: 1px solid #f0f0f0; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #f0f0f0; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-of-type(odd) { background-color: #f4f4f4; } .table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { position: static; float: none; display: table-column; } table td[class*="col-"], table th[class*="col-"] { position: static; float: none; display: table-cell; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #fffcbe; } .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #fffba4; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #67bd6a; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #55b559; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #ffa829; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #ff9e0f; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f6675d; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #f55145; } .table-responsive { overflow-x: auto; min-height: 0.01%; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 13.5px; overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #f0f0f0; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { padding: 0; margin: 0; border: 0; min-width: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 18px; font-size: 19.5px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } output { display: block; padding-top: 7px; font-size: 13px; line-height: 1.42857143; color: #000000; } .form-control { display: block; width: 100%; height: 35px; padding: 6px 12px; font-size: 13px; line-height: 1.42857143; color: #000000; background-color: #ffffff; background-image: none; border: 1px solid #e0e0e0; border-radius: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #b4b4b4; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(180, 180, 180, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(180, 180, 180, 0.6); } .form-control::-moz-placeholder { color: #999999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control::-ms-expand { border: 0; background-color: transparent; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: #eeeeee; opacity: 1; } .form-control[disabled], fieldset[disabled] .form-control { cursor: not-allowed; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { line-height: 35px; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { line-height: 30px; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { line-height: 40px; } } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { min-height: 18px; padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-left: -20px; margin-top: 4px \9; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { position: relative; display: inline-block; padding-left: 20px; margin-bottom: 0; vertical-align: middle; font-weight: normal; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; min-height: 31px; } .form-control-static.input-lg, .form-control-static.input-sm { padding-left: 0; padding-right: 0; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm, select[multiple].input-sm { height: auto; } .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .form-group-sm select.form-control { height: 30px; line-height: 30px; } .form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control { height: auto; } .form-group-sm .form-control-static { height: 30px; min-height: 30px; padding: 6px 10px; font-size: 12px; line-height: 1.5; } .input-lg { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } select.input-lg { height: 40px; line-height: 40px; } textarea.input-lg, select[multiple].input-lg { height: auto; } .form-group-lg .form-control { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } .form-group-lg select.form-control { height: 40px; line-height: 40px; } .form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control { height: auto; } .form-group-lg .form-control-static { height: 40px; min-height: 35px; padding: 11px 16px; font-size: 17px; line-height: 1.3333333; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 43.75px; } .form-control-feedback { position: absolute; top: 0; right: 0; z-index: 2; display: block; width: 35px; height: 35px; line-height: 35px; text-align: center; pointer-events: none; } .input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback { width: 40px; height: 40px; line-height: 40px; } .input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { color: #67bd6a; } .has-success .form-control { border-color: #67bd6a; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #49a84d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #aedcb0; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #aedcb0; } .has-success .input-group-addon { color: #67bd6a; border-color: #67bd6a; background-color: #67bd6a; } .has-success .form-control-feedback { color: #67bd6a; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { color: #ffa829; } .has-warning .form-control { border-color: #ffa829; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #f59200; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffd28f; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffd28f; } .has-warning .input-group-addon { color: #ffa829; border-color: #ffa829; background-color: #ffa829; } .has-warning .form-control-feedback { color: #ffa829; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { color: #f6675d; } .has-error .form-control { border-color: #f6675d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #f33a2c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #fbc2bd; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #fbc2bd; } .has-error .input-group-addon { color: #f6675d; border-color: #f6675d; background-color: #f6675d; } .has-error .form-control-feedback { color: #f6675d; } .has-feedback label ~ .form-control-feedback { top: 23px; } .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #9e9e9e; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .form-control-static { display: inline-block; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { margin-top: 0; margin-bottom: 0; padding-top: 7px; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 25px; } .form-horizontal .form-group { margin-left: -15px; margin-right: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; margin-bottom: 0; padding-top: 7px; } } .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 11px; font-size: 17px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; font-size: 12px; } } .btn { display: inline-block; margin-bottom: 0; font-weight: 400; text-align: center; vertical-align: middle; touch-action: manipulation; cursor: pointer; background-image: none; border: 1px solid transparent; white-space: nowrap; padding: 6px 12px; font-size: 13px; line-height: 1.42857143; border-radius: 2px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; outline: none !important; } .btn:hover, .btn:focus, .btn.focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { outline: 0; background-image: none; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { cursor: not-allowed; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } a.btn.disabled, fieldset[disabled] a.btn { pointer-events: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:focus, .btn-default.focus { color: #333333; background-color: #e6e6e6; border-color: #8c8c8c; } .btn-default:hover { color: #333333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { color: #333333; background-color: #d4d4d4; border-color: #8c8c8c; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #333333; } .btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .open > .dropdown-toggle.btn-default { color: #333333; background-color: #ffffff; border-color: transparent; } .btn-default:hover:hover, .btn-default:focus:hover, .btn-default.focus:hover, .btn-default:active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:hover:focus, .btn-default:focus:focus, .btn-default.focus:focus, .btn-default:active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:hover.focus, .btn-default:focus.focus, .btn-default.focus.focus, .btn-default:active.focus, .open > .dropdown-toggle.btn-default.focus { color: #333333; background-color: #ffffff; border-color: transparent; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #333333; } .btn-primary { color: #ffffff; background-color: #2196f3; border-color: #0d8aee; } .btn-primary:focus, .btn-primary.focus { color: #ffffff; background-color: #0c7cd5; border-color: #064475; } .btn-primary:hover { color: #ffffff; background-color: #0c7cd5; border-color: #0a68b4; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #ffffff; background-color: #0c7cd5; border-color: #0a68b4; } .btn-primary:active:hover, .btn-primary.active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:active:focus, .btn-primary.active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:active.focus, .btn-primary.active.focus, .open > .dropdown-toggle.btn-primary.focus { color: #ffffff; background-color: #0a68b4; border-color: #064475; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus { background-color: #2196f3; border-color: #0d8aee; } .btn-primary .badge { color: #2196f3; background-color: #ffffff; } .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .open > .dropdown-toggle.btn-primary { color: #ffffff; background-color: #2196f3; border-color: transparent; } .btn-primary:hover:hover, .btn-primary:focus:hover, .btn-primary.focus:hover, .btn-primary:active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:hover:focus, .btn-primary:focus:focus, .btn-primary.focus:focus, .btn-primary:active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:hover.focus, .btn-primary:focus.focus, .btn-primary.focus.focus, .btn-primary:active.focus, .open > .dropdown-toggle.btn-primary.focus { color: #ffffff; background-color: #2196f3; border-color: transparent; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active { background-color: #2196f3; border-color: #0d8aee; } .btn-primary .badge { color: #2196f3; background-color: #ffffff; } .btn-success { color: #ffffff; background-color: #4caf50; border-color: #449d48; } .btn-success:focus, .btn-success.focus { color: #ffffff; background-color: #3d8b40; border-color: #1e441f; } .btn-success:hover { color: #ffffff; background-color: #3d8b40; border-color: #327334; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #ffffff; background-color: #3d8b40; border-color: #327334; } .btn-success:active:hover, .btn-success.active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:active:focus, .btn-success.active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:active.focus, .btn-success.active.focus, .open > .dropdown-toggle.btn-success.focus { color: #ffffff; background-color: #327334; border-color: #1e441f; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus { background-color: #4caf50; border-color: #449d48; } .btn-success .badge { color: #4caf50; background-color: #ffffff; } .btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .open > .dropdown-toggle.btn-success { color: #ffffff; background-color: #4caf50; border-color: transparent; } .btn-success:hover:hover, .btn-success:focus:hover, .btn-success.focus:hover, .btn-success:active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:hover:focus, .btn-success:focus:focus, .btn-success.focus:focus, .btn-success:active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:hover.focus, .btn-success:focus.focus, .btn-success.focus.focus, .btn-success:active.focus, .open > .dropdown-toggle.btn-success.focus { color: #ffffff; background-color: #4caf50; border-color: transparent; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active { background-color: #4caf50; border-color: #449d48; } .btn-success .badge { color: #4caf50; background-color: #ffffff; } .btn-info { color: #ffffff; background-color: #00bcd4; border-color: #00a5bb; } .btn-info:focus, .btn-info.focus { color: #ffffff; background-color: #008fa1; border-color: #00343b; } .btn-info:hover { color: #ffffff; background-color: #008fa1; border-color: #006f7d; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #ffffff; background-color: #008fa1; border-color: #006f7d; } .btn-info:active:hover, .btn-info.active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:active:focus, .btn-info.active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { color: #ffffff; background-color: #006f7d; border-color: #00343b; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus { background-color: #00bcd4; border-color: #00a5bb; } .btn-info .badge { color: #00bcd4; background-color: #ffffff; } .btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .open > .dropdown-toggle.btn-info { color: #ffffff; background-color: #00bcd4; border-color: transparent; } .btn-info:hover:hover, .btn-info:focus:hover, .btn-info.focus:hover, .btn-info:active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:hover:focus, .btn-info:focus:focus, .btn-info.focus:focus, .btn-info:active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:hover.focus, .btn-info:focus.focus, .btn-info.focus.focus, .btn-info:active.focus, .open > .dropdown-toggle.btn-info.focus { color: #ffffff; background-color: #00bcd4; border-color: transparent; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active { background-color: #00bcd4; border-color: #00a5bb; } .btn-info .badge { color: #00bcd4; background-color: #ffffff; } .btn-warning { color: #ffffff; background-color: #ff9800; border-color: #e68900; } .btn-warning:focus, .btn-warning.focus { color: #ffffff; background-color: #cc7a00; border-color: #663d00; } .btn-warning:hover { color: #ffffff; background-color: #cc7a00; border-color: #a86400; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #ffffff; background-color: #cc7a00; border-color: #a86400; } .btn-warning:active:hover, .btn-warning.active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:active:focus, .btn-warning.active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:active.focus, .btn-warning.active.focus, .open > .dropdown-toggle.btn-warning.focus { color: #ffffff; background-color: #a86400; border-color: #663d00; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus { background-color: #ff9800; border-color: #e68900; } .btn-warning .badge { color: #ff9800; background-color: #ffffff; } .btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .open > .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ff9800; border-color: transparent; } .btn-warning:hover:hover, .btn-warning:focus:hover, .btn-warning.focus:hover, .btn-warning:active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:hover:focus, .btn-warning:focus:focus, .btn-warning.focus:focus, .btn-warning:active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:hover.focus, .btn-warning:focus.focus, .btn-warning.focus.focus, .btn-warning:active.focus, .open > .dropdown-toggle.btn-warning.focus { color: #ffffff; background-color: #ff9800; border-color: transparent; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active { background-color: #ff9800; border-color: #e68900; } .btn-warning .badge { color: #ff9800; background-color: #ffffff; } .btn-danger { color: #ffffff; background-color: #f44336; border-color: #f32c1e; } .btn-danger:focus, .btn-danger.focus { color: #ffffff; background-color: #ea1c0d; border-color: #891008; } .btn-danger:hover { color: #ffffff; background-color: #ea1c0d; border-color: #c8180b; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #ffffff; background-color: #ea1c0d; border-color: #c8180b; } .btn-danger:active:hover, .btn-danger.active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:active:focus, .btn-danger.active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:active.focus, .btn-danger.active.focus, .open > .dropdown-toggle.btn-danger.focus { color: #ffffff; background-color: #c8180b; border-color: #891008; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus { background-color: #f44336; border-color: #f32c1e; } .btn-danger .badge { color: #f44336; background-color: #ffffff; } .btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .open > .dropdown-toggle.btn-danger { color: #ffffff; background-color: #f44336; border-color: transparent; } .btn-danger:hover:hover, .btn-danger:focus:hover, .btn-danger.focus:hover, .btn-danger:active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:hover:focus, .btn-danger:focus:focus, .btn-danger.focus:focus, .btn-danger:active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:hover.focus, .btn-danger:focus.focus, .btn-danger.focus.focus, .btn-danger:active.focus, .open > .dropdown-toggle.btn-danger.focus { color: #ffffff; background-color: #f44336; border-color: transparent; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active { background-color: #f44336; border-color: #f32c1e; } .btn-danger .badge { color: #f44336; background-color: #ffffff; } .btn-link { color: #2196f3; font-weight: normal; border-radius: 0; } .btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #0a6ebd; text-decoration: none; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #777777; text-decoration: none; } .btn-lg, .btn-group-lg > .btn { padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 2px; } .btn-sm, .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .btn-xs, .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 2px; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; -o-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } tr.collapse.in { display: table-row; } tbody.collapse.in { display: table-row-group; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition-property: height, visibility; transition-property: height, visibility; -webkit-transition-duration: 0.35s; transition-duration: 0.35s; -webkit-transition-timing-function: ease; transition-timing-function: ease; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px dashed; border-top: 4px solid \9; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropup, .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 9; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; list-style: none; font-size: 13px; text-align: left; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 8px 0; overflow: hidden; background-color: rgba(0, 0, 0, 0.08); } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.42857143; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { text-decoration: none; color: #333333; background-color: rgba(0, 0, 0, 0.075); } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #333333; text-decoration: none; outline: 0; background-color: rgba(0, 0, 0, 0.075); } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #e4e4e4; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); cursor: not-allowed; } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-menu-right { left: auto; right: 0; } .dropdown-menu-left { left: 0; right: auto; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.42857143; color: #777777; white-space: nowrap; } .dropdown-backdrop { position: fixed; left: 0; right: 0; bottom: 0; top: 0; z-index: -1; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px dashed; border-bottom: 4px solid \9; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 2px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { left: auto; right: 0; } .navbar-right .dropdown-menu-left { left: 0; right: auto; } } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar { margin-left: -5px; } .btn-toolbar .btn, .btn-toolbar .btn-group, .btn-toolbar .input-group { float: left; } .btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-bottom-right-radius: 0; border-top-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-top-right-radius: 0; } .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .btn + .dropdown-toggle { padding-left: 8px; padding-right: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-left: 12px; padding-right: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 2px; border-top-left-radius: 2px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-right-radius: 0; border-top-left-radius: 0; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { float: none; display: table-cell; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn input[type="radio"], [data-toggle="buttons"] > .btn-group > .btn input[type="radio"], [data-toggle="buttons"] > .btn input[type="checkbox"], [data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-left: 0; padding-right: 0; } .input-group .form-control { position: relative; z-index: 2; float: left; width: 100%; margin-bottom: 0; } .input-group .form-control:focus { z-index: 3; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 40px; padding: 10px 16px; font-size: 17px; line-height: 1.3333333; border-radius: 0px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 40px; line-height: 40px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 2px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 13px; font-weight: normal; line-height: 1; color: #000000; text-align: center; background-color: transparent; border: 1px solid transparent; border-radius: 0; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 2px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 17px; border-radius: 0px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-top-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { border-bottom-left-radius: 0; border-top-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; font-size: 0; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -1px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { z-index: 2; } .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { margin-right: -1px; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { z-index: 2; margin-left: -1px; } .nav { margin-bottom: 0; padding-left: 0; list-style: none; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li.disabled > a { color: #777777; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #777777; text-decoration: none; background-color: transparent; cursor: not-allowed; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eeeeee; border-color: #2196f3; } .nav .nav-divider { height: 1px; margin: 8px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #ffffff; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.42857143; border: 1px solid transparent; border-radius: 2px 2px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #ffffff; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555555; background-color: transparent; border: 1px solid #ffffff; border-bottom-color: transparent; cursor: default; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { text-align: center; margin-bottom: 5px; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 2px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 2px 2px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #edecec; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 2px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #ffffff; background-color: #2196f3; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { text-align: center; margin-bottom: 5px; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 2px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 2px 2px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #edecec; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 18px; border: 1px solid transparent; } @media (min-width: 768px) { .navbar { border-radius: 2px; } } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { overflow-x: visible; padding-right: 15px; padding-left: 15px; border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -webkit-overflow-scrolling: touch; } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-left: 0; padding-right: 0; } } .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } @media (max-device-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; } } .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; padding: 16px 15px; font-size: 17px; line-height: 18px; height: 50px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } .navbar-brand > img { display: block; } @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; margin-right: 15px; padding: 9px 10px; margin-top: 8px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 2px; } .navbar-toggle:focus { outline: 0; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 8px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 18px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 18px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 16px; padding-bottom: 16px; } } .navbar-form { margin-left: -15px; margin-right: -15px; padding: 10px 15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); margin-top: 7.5px; margin-bottom: 7.5px; } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; width: auto; vertical-align: middle; } .navbar-form .form-control-static { display: inline-block; } .navbar-form .input-group { display: inline-table; vertical-align: middle; } .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { width: auto; } .navbar-form .input-group > .form-control { width: 100%; } .navbar-form .control-label { margin-bottom: 0; vertical-align: middle; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .navbar-form .radio label, .navbar-form .checkbox label { padding-left: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .navbar-form .has-feedback .form-control-feedback { top: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } .navbar-form .form-group:last-child { margin-bottom: 0; } } @media (min-width: 768px) { .navbar-form { width: auto; border: 0; margin-left: 0; margin-right: 0; padding-top: 0; padding-bottom: 0; -webkit-box-shadow: none; box-shadow: none; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { margin-bottom: 0; border-top-right-radius: 2px; border-top-left-radius: 2px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-btn { margin-top: 7.5px; margin-bottom: 7.5px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 16px; margin-bottom: 16px; } @media (min-width: 768px) { .navbar-text { float: left; margin-left: 15px; margin-right: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: -15px; } .navbar-right ~ .navbar-right { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777777; } .navbar-default .navbar-nav > li > a { color: #777777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #cccccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #dddddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #dddddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #888888; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { background-color: #e7e7e7; color: #555555; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #cccccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777777; } .navbar-default .navbar-link:hover { color: #333333; } .navbar-default .btn-link { color: #777777; } .navbar-default .btn-link:hover, .navbar-default .btn-link:focus { color: #333333; } .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { color: #cccccc; } .navbar-inverse { background-color: #222222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #9d9d9d; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-text { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #ffffff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { background-color: #080808; color: #ffffff; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #9d9d9d; } .navbar-inverse .navbar-link:hover { color: #ffffff; } .navbar-inverse .btn-link { color: #9d9d9d; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { color: #ffffff; } .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { color: #444444; } .breadcrumb { padding: 8px 20px; margin-bottom: 18px; list-style: none; background-color: transparent; border-radius: 2px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { content: "/\00a0"; padding: 0 5px; color: #cccccc; } .breadcrumb > .active { color: #7c7c7c; } .pagination { display: inline-block; padding-left: 0; margin: 18px 0; border-radius: 2px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; line-height: 1.42857143; text-decoration: none; color: #7e7e7e; background-color: #e2e2e2; border: 1px solid #ffffff; margin-left: -1px; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { z-index: 2; color: #333333; background-color: #d7d7d7; border-color: #ffffff; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 3; color: #ffffff; background-color: #00bcd4; border-color: #ffffff; cursor: default; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #777777; background-color: #e2e2e2; border-color: #ffffff; cursor: not-allowed; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 17px; line-height: 1.3333333; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; line-height: 1.5; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-bottom-left-radius: 2px; border-top-left-radius: 2px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-bottom-right-radius: 2px; border-top-right-radius: 2px; } .pager { padding-left: 0; margin: 18px 0; list-style: none; text-align: center; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #e2e2e2; border: 1px solid #ffffff; border-radius: 5px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #d7d7d7; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #777777; background-color: #e2e2e2; cursor: not-allowed; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #2196f3; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #0c7cd5; } .label-success { background-color: #4caf50; } .label-success[href]:hover, .label-success[href]:focus { background-color: #3d8b40; } .label-info { background-color: #00bcd4; } .label-info[href]:hover, .label-info[href]:focus { background-color: #008fa1; } .label-warning { background-color: #ff9800; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #cc7a00; } .label-danger { background-color: #f44336; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #ea1c0d; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: 400; color: #ffffff; line-height: 1; vertical-align: middle; white-space: nowrap; text-align: center; background-color: #2196f3; border-radius: 2px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .btn-xs .badge, .btn-group-xs > .btn .badge { top: 0; padding: 1px 5px; } a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #ffffff; background-color: #2196f3; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding-top: 30px; padding-bottom: 30px; margin-bottom: 30px; color: inherit; background-color: #f7f7f7; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 20px; font-weight: 200; } .jumbotron > hr { border-top-color: #dedede; } .container .jumbotron, .container-fluid .jumbotron { border-radius: 2px; padding-left: 15px; padding-right: 15px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron, .container-fluid .jumbotron { padding-left: 60px; padding-right: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 59px; } } .thumbnail { display: block; padding: 3px; margin-bottom: 18px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #ededed; border-radius: 2px; -webkit-transition: border 0.2s ease-in-out; -o-transition: border 0.2s ease-in-out; transition: border 0.2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-left: auto; margin-right: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #2196f3; } .thumbnail .caption { padding: 9px; color: #5e5e5e; } .alert { padding: 15px; margin-bottom: 18px; border: 1px solid transparent; border-radius: 2px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { background-color: rgba(76, 175, 80, 0.7); border-color: transparent; color: #ffffff; } .alert-success hr { border-top-color: rgba(0, 0, 0, 0); } .alert-success .alert-link { color: #e6e6e6; } .alert-info { background-color: rgba(33, 150, 243, 0.7); border-color: transparent; color: #ffffff; } .alert-info hr { border-top-color: rgba(0, 0, 0, 0); } .alert-info .alert-link { color: #e6e6e6; } .alert-warning { background-color: rgba(255, 193, 7, 0.7); border-color: transparent; color: #ffffff; } .alert-warning hr { border-top-color: rgba(0, 0, 0, 0); } .alert-warning .alert-link { color: #e6e6e6; } .alert-danger { background-color: rgba(244, 67, 54, 0.7); border-color: transparent; color: #ffffff; } .alert-danger hr { border-top-color: rgba(0, 0, 0, 0); } .alert-danger .alert-link { color: #e6e6e6; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { overflow: hidden; height: 18px; margin-bottom: 18px; background-color: #f5f5f5; border-radius: 2px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress-bar { float: left; width: 0%; height: 100%; font-size: 12px; line-height: 18px; color: #ffffff; text-align: center; background-color: #2196f3; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-transition: width 0.6s ease; -o-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .progress-bar, .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: 40px 40px; } .progress.active .progress-bar, .progress-bar.active { -webkit-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #4caf50; } .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #00bcd4; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #ff9800; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #f44336; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media, .media-body { zoom: 1; overflow: hidden; } .media-body { width: 10000px; } .media-object { display: block; } .media-object.img-thumbnail { max-width: none; } .media-right, .media > .pull-right { padding-left: 10px; } .media-left, .media > .pull-left { padding-right: 10px; } .media-left, .media-right, .media-body { display: table-cell; vertical-align: top; } .media-middle { vertical-align: middle; } .media-bottom { vertical-align: bottom; } .media-heading { margin-top: 0; margin-bottom: 5px; } .media-list { padding-left: 0; list-style: none; } .list-group { margin-bottom: 20px; padding-left: 0; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #ffffff; border: 1px solid #e9e9e9; } .list-group-item:first-child { border-top-right-radius: 2px; border-top-left-radius: 2px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; } a.list-group-item, button.list-group-item { color: #555555; } a.list-group-item .list-group-item-heading, button.list-group-item .list-group-item-heading { color: #333333; } a.list-group-item:hover, button.list-group-item:hover, a.list-group-item:focus, button.list-group-item:focus { text-decoration: none; color: #555555; background-color: #f5f5f5; } button.list-group-item { width: 100%; text-align: left; } .list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { background-color: #ffffff; color: #b5b4b4; cursor: not-allowed; } .list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { color: inherit; } .list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { color: #b5b4b4; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #000000; background-color: #f5f5f5; border-color: #e9e9e9; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #ffffff; } .list-group-item-success { color: #67bd6a; background-color: #67bd6a; } a.list-group-item-success, button.list-group-item-success { color: #67bd6a; } a.list-group-item-success .list-group-item-heading, button.list-group-item-success .list-group-item-heading { color: inherit; } a.list-group-item-success:hover, button.list-group-item-success:hover, a.list-group-item-success:focus, button.list-group-item-success:focus { color: #67bd6a; background-color: #55b559; } a.list-group-item-success.active, button.list-group-item-success.active, a.list-group-item-success.active:hover, button.list-group-item-success.active:hover, a.list-group-item-success.active:focus, button.list-group-item-success.active:focus { color: #fff; background-color: #67bd6a; border-color: #67bd6a; } .list-group-item-info { color: #31708f; background-color: #d9edf7; } a.list-group-item-info, button.list-group-item-info { color: #31708f; } a.list-group-item-info .list-group-item-heading, button.list-group-item-info .list-group-item-heading { color: inherit; } a.list-group-item-info:hover, button.list-group-item-info:hover, a.list-group-item-info:focus, button.list-group-item-info:focus { color: #31708f; background-color: #c4e3f3; } a.list-group-item-info.active, button.list-group-item-info.active, a.list-group-item-info.active:hover, button.list-group-item-info.active:hover, a.list-group-item-info.active:focus, button.list-group-item-info.active:focus { color: #fff; background-color: #31708f; border-color: #31708f; } .list-group-item-warning { color: #ffa829; background-color: #ffa829; } a.list-group-item-warning, button.list-group-item-warning { color: #ffa829; } a.list-group-item-warning .list-group-item-heading, button.list-group-item-warning .list-group-item-heading { color: inherit; } a.list-group-item-warning:hover, button.list-group-item-warning:hover, a.list-group-item-warning:focus, button.list-group-item-warning:focus { color: #ffa829; background-color: #ff9e0f; } a.list-group-item-warning.active, button.list-group-item-warning.active, a.list-group-item-warning.active:hover, button.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus, button.list-group-item-warning.active:focus { color: #fff; background-color: #ffa829; border-color: #ffa829; } .list-group-item-danger { color: #f6675d; background-color: #f6675d; } a.list-group-item-danger, button.list-group-item-danger { color: #f6675d; } a.list-group-item-danger .list-group-item-heading, button.list-group-item-danger .list-group-item-heading { color: inherit; } a.list-group-item-danger:hover, button.list-group-item-danger:hover, a.list-group-item-danger:focus, button.list-group-item-danger:focus { color: #f6675d; background-color: #f55145; } a.list-group-item-danger.active, button.list-group-item-danger.active, a.list-group-item-danger.active:hover, button.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus, button.list-group-item-danger.active:focus { color: #fff; background-color: #f6675d; border-color: #f6675d; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 18px; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 15px; color: inherit; } .panel-title > a, .panel-title > small, .panel-title > .small, .panel-title > small > a, .panel-title > .small > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .list-group, .panel > .panel-collapse > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { padding-left: 15px; padding-right: 15px; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-right-radius: 1px; border-top-left-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { border-top-left-radius: 1px; border-top-right-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 1px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 1px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { border-bottom-left-radius: 1px; border-bottom-right-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 1px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 1px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { border-top: 1px solid #f0f0f0; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { border: 0; margin-bottom: 0; } .panel-group { margin-bottom: 18px; } .panel-group .panel { margin-bottom: 0; border-radius: 2px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse > .panel-body, .panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-default { border-color: #dddddd; } .panel-default > .panel-heading { color: #333333; background-color: #f5f5f5; border-color: #dddddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #dddddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #dddddd; } .panel-primary { border-color: #2196f3; } .panel-primary > .panel-heading { color: #ffffff; background-color: #2196f3; border-color: #2196f3; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #2196f3; } .panel-primary > .panel-heading .badge { color: #2196f3; background-color: #ffffff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #2196f3; } .panel-success { border-color: #61b555; } .panel-success > .panel-heading { color: #67bd6a; background-color: #67bd6a; border-color: #61b555; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #61b555; } .panel-success > .panel-heading .badge { color: #67bd6a; background-color: #67bd6a; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #61b555; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #ff760f; } .panel-warning > .panel-heading { color: #ffa829; background-color: #ffa829; border-color: #ff760f; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ff760f; } .panel-warning > .panel-heading .badge { color: #ffa829; background-color: #ffa829; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ff760f; } .panel-danger { border-color: #f54556; } .panel-danger > .panel-heading { color: #f6675d; background-color: #f6675d; border-color: #f54556; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #f54556; } .panel-danger > .panel-heading .badge { color: #f6675d; background-color: #f6675d; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #f54556; } .embed-responsive { position: relative; display: block; height: 0; padding: 0; overflow: hidden; } .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video { position: absolute; top: 0; left: 0; bottom: 0; height: 100%; width: 100%; border: 0; } .embed-responsive-16by9 { padding-bottom: 56.25%; } .embed-responsive-4by3 { padding-bottom: 75%; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 2px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-lg { padding: 24px; border-radius: 2px; } .well-sm { padding: 9px; border-radius: 2px; } .close { float: right; font-size: 19.5px; font-weight: bold; line-height: 1; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.5; filter: alpha(opacity=50); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .modal-open { overflow: hidden; } .modal { display: none; overflow: hidden; position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; -webkit-overflow-scrolling: touch; outline: 0; } .modal.fade .modal-dialog { -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); -o-transform: translate(0, -25%); transform: translate(0, -25%); -webkit-transition: -webkit-transform 0.3s ease-out; -moz-transition: -moz-transform 0.3s ease-out; -o-transition: -o-transform 0.3s ease-out; transition: transform 0.3s ease-out; } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); -o-transform: translate(0, 0); transform: translate(0, 0); } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #ffffff; border: 1px solid transparent; border-radius: 2px; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); background-clip: padding-box; outline: 0; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 11; background-color: #000000; } .modal-backdrop.fade { opacity: 0; filter: alpha(opacity=0); } .modal-backdrop.in { opacity: 0.5; filter: alpha(opacity=50); } .modal-header { padding: 15px; border-bottom: 1px solid transparent; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: transparent; } .modal-body { position: relative; padding: 15px; } .modal-footer { padding: 15px; text-align: right; border-top: 1px solid transparent; } .modal-footer .btn + .btn { margin-left: 5px; margin-bottom: 0; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } .modal-sm { width: 300px; } } @media (min-width: 992px) { .modal-lg { width: 900px; } } .tooltip { position: absolute; z-index: 1070; display: block; font-family: roboto; font-style: normal; font-weight: normal; letter-spacing: normal; line-break: auto; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; white-space: normal; word-break: normal; word-spacing: normal; word-wrap: normal; font-size: 12px; opacity: 0; filter: alpha(opacity=0); } .tooltip.in { opacity: 1; filter: alpha(opacity=100); } .tooltip.top { margin-top: -3px; padding: 5px 0; } .tooltip.right { margin-left: 3px; padding: 0 5px; } .tooltip.bottom { margin-top: 3px; padding: 5px 0; } .tooltip.left { margin-left: -3px; padding: 0 5px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; background-color: #737373; border-radius: 2px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.top-left .tooltip-arrow { bottom: 0; right: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.top-right .tooltip-arrow { bottom: 0; left: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #737373; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-width: 5px 5px 5px 0; border-right-color: #737373; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-width: 5px 0 5px 5px; border-left-color: #737373; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .tooltip.bottom-left .tooltip-arrow { top: 0; right: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .tooltip.bottom-right .tooltip-arrow { top: 0; left: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #737373; } .popover { position: absolute; top: 0; left: 0; z-index: 9; display: none; max-width: 276px; padding: 1px; font-family: roboto; font-style: normal; font-weight: normal; letter-spacing: normal; line-break: auto; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; white-space: normal; word-break: normal; word-spacing: normal; word-wrap: normal; font-size: 13px; background-color: #ffffff; background-clip: padding-box; border: 1px solid #ffffff; border-radius: 2px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { margin: 0; padding: 8px 14px; font-size: 13px; background-color: #ffffff; border-bottom: 1px solid #f2f2f2; border-radius: 1px 1px 0 0; } .popover-content { padding: 9px 14px; } .popover > .arrow, .popover > .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover > .arrow { border-width: 11px; } .popover > .arrow:after { border-width: 10px; content: ""; } .popover.top > .arrow { left: 50%; margin-left: -11px; border-bottom-width: 0; border-top-color: #cccccc; border-top-color: #ffffff; bottom: -11px; } .popover.top > .arrow:after { content: " "; bottom: 1px; margin-left: -10px; border-bottom-width: 0; border-top-color: #ffffff; } .popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; border-left-width: 0; border-right-color: #cccccc; border-right-color: #ffffff; } .popover.right > .arrow:after { content: " "; left: 1px; bottom: -10px; border-left-width: 0; border-right-color: #ffffff; } .popover.bottom > .arrow { left: 50%; margin-left: -11px; border-top-width: 0; border-bottom-color: #cccccc; border-bottom-color: #ffffff; top: -11px; } .popover.bottom > .arrow:after { content: " "; top: 1px; margin-left: -10px; border-top-width: 0; border-bottom-color: #ffffff; } .popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; border-right-width: 0; border-left-color: #cccccc; border-left-color: #ffffff; } .popover.left > .arrow:after { content: " "; right: 1px; border-right-width: 0; border-left-color: #ffffff; bottom: -10px; } .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; } .carousel-inner > .item { display: none; position: relative; -webkit-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } @media all and (transform-3d), (-webkit-transform-3d) { .carousel-inner > .item { -webkit-transition: -webkit-transform 0.6s ease-in-out; -moz-transition: -moz-transform 0.6s ease-in-out; -o-transition: -o-transform 0.6s ease-in-out; transition: transform 0.6s ease-in-out; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; perspective: 1000px; } .carousel-inner > .item.next, .carousel-inner > .item.active.right { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); left: 0; } .carousel-inner > .item.prev, .carousel-inner > .item.active.left { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); left: 0; } .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); left: 0; } } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: 15%; opacity: 0.5; filter: alpha(opacity=50); font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0); } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { left: auto; right: 0; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { outline: 0; color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; margin-top: -10px; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; line-height: 1; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid #ffffff; border-radius: 10px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); } .carousel-indicators .active { margin: 0; width: 12px; height: 12px; background-color: #ffffff; } .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -10px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -10px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -10px; } .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-header:before, .modal-header:after, .modal-footer:before, .modal-footer:after { content: " "; display: table; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-header:after, .modal-footer:after { clear: both; } .center-block { display: block; margin-left: auto; margin-right: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table !important; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table !important; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table !important; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table !important; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table !important; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } /* * LESS Plugins */ /* * Variable and Mixin */ /* * Font Icon Family */ /* * Grid System */ /* Typography + Scaffolding + Links */ /* Border Radius */ /* Tabs */ /* Form */ /* Table */ /* * Input Group */ /* * Pagination */ /* * Popover */ /* * Dropdown */ /* * Thumbnail */ /* * Alerts */ /* * Form Validations */ /* * Buttons */ /* * Thumbnail */ /* * Carousel */ /* * Modal */ /* * Tooltips */ /* * Popover */ /* * Breadcrumbs */ /* * Jumbotron */ /* * List Groups */ /* * Badges */ /* * Material Colors */ /* Bootstrap Branding */ /* * Colors */ /* * Blocks */ /* * Misc */ /* * Font Face */ /* * Background Repeat + Position */ /* * CSS Animations based on animate.css */ /* * CSS Transform - Scale and Rotate */ /* * User Select */ /* * Background Image Cover */ /* * Tab Focus */ /* * Pop-in Hover effects */ /* * Override Bootstrap Button Mixin */ /* * Scale 3d */ /* * Load Font */ /* * Roboto Light */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Light-webfont.eot'); src: url('../fonts/roboto/Roboto-Light-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Light-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Light-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Light-webfont.svg#icon') format('svg'); font-weight: 300; font-style: normal; } /* * Roboto Regular */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Regular-webfont.eot'); src: url('../fonts/roboto/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Regular-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Regular-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Regular-webfont.svg#icon') format('svg'); font-weight: 400; font-style: normal; } /* * Roboto Medium */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Medium-webfont.eot'); src: url('../fonts/roboto/Roboto-Medium-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Medium-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Medium-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Medium-webfont.svg#icon') format('svg'); font-weight: 500; font-style: normal; } /* * Roboto Bold */ @font-face { font-family: roboto; src: url('../fonts/roboto/Roboto-Bold-webfont.eot'); src: url('../fonts/roboto/Roboto-Bold-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/roboto/Roboto-Bold-webfont.woff') format('woff'), url('../fonts/roboto/Roboto-Bold-webfont.ttf') format('truetype'), url('../fonts/roboto/Roboto-Bold-webfont.svg#icon') format('svg'); font-weight: 700; font-style: normal; } /* * Shadow Light */ @font-face { font-family: shadowsintolight; src: url('../fonts/shadowsintolight/shadowsintolight-webfont.eot'); src: url('../fonts/shadowsintolight/shadowsintolight-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/shadowsintolight/shadowsintolight-webfont.woff') format('woff'), url('../fonts/shadowsintolight/shadowsintolight-webfont.ttf') format('truetype'), url('../fonts/shadowsintolight/shadowsintolight-webfont.svg#icon') format('svg'); font-weight: 400; font-style: normal; } /* * Vendors */ @font-face { font-family: weather-icons; src: url('../fonts/weather-icons/weather-icons.eot'); src: url('../fonts/weather-icons/weather-icons.eot?#iefix') format('embedded-opentype'), url('../fonts/weather-icons/weather-icons.woff') format('woff'), url('../fonts/weather-icons/weather-icons.ttf') format('truetype'), url('../fonts/weather-icons/weather-icons.svg#icon') format('svg'); font-weight: 400; font-style: normal; } #weather-widget [class*="icon-"] { font-family: 'weather-icons'; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-0:before { content: ":"; } .icon-1:before { content: "p"; } .icon-2:before { content: "S"; } .icon-3:before { content: "Q"; } .icon-4:before { content: "S"; } .icon-5:before { content: "W"; } .icon-6:before { content: "W"; } .icon-7:before { content: "W"; } .icon-8:before { content: "W"; } .icon-9:before { content: "I"; } .icon-10:before { content: "W"; } .icon-11:before { content: "I"; } .icon-12:before { content: "I"; } .icon-13:before { content: "I"; } .icon-14:before { content: "I"; } .icon-15:before { content: "W"; } .icon-16:before { content: "I"; } .icon-17:before { content: "W"; } .icon-18:before { content: "U"; } .icon-19:before { content: "Z"; } .icon-20:before { content: "Z"; } .icon-21:before { content: "Z"; } .icon-22:before { content: "Z"; } .icon-23:before { content: "Z"; } .icon-24:before { content: "E"; } .icon-25:before { content: "E"; } .icon-26:before { content: "3"; } .icon-27:before { content: "a"; } .icon-28:before { content: "A"; } .icon-29:before { content: "a"; } .icon-30:before { content: "A"; } .icon-31:before { content: "6"; } .icon-32:before { content: "1"; } .icon-33:before { content: "6"; } .icon-34:before { content: "1"; } .icon-35:before { content: "W"; } .icon-36:before { content: "1"; } .icon-37:before { content: "S"; } .icon-38:before { content: "S"; } .icon-39:before { content: "S"; } .icon-40:before { content: "M"; } .icon-41:before { content: "W"; } .icon-42:before { content: "I"; } .icon-43:before { content: "W"; } .icon-44:before { content: "a"; } .icon-45:before { content: "S"; } .icon-46:before { content: "U"; } .icon-47:before { content: "S"; } .btn-file { overflow: hidden; position: relative; vertical-align: middle; } .btn-file > input { position: absolute; top: 0; right: 0; margin: 0; opacity: 0; filter: alpha(opacity=0); font-size: 23px; height: 100%; width: 100%; direction: ltr; cursor: pointer; } .fileinput { margin-bottom: 9px; display: inline-block; } .fileinput .form-control { padding-top: 7px; padding-bottom: 5px; display: inline-block; margin-bottom: 0px; vertical-align: middle; cursor: text; } .fileinput .thumbnail { overflow: hidden; display: inline-block; margin-bottom: 5px; vertical-align: middle; text-align: center; } .fileinput .thumbnail > img { max-height: 100%; } .fileinput .btn { vertical-align: middle; } .fileinput-exists .fileinput-new, .fileinput-new .fileinput-exists { display: none; } .fileinput-inline .fileinput-controls { display: inline; } .fileinput-filename { vertical-align: middle; display: inline-block; overflow: hidden; } .form-control .fileinput-filename { vertical-align: bottom; } .fileinput.input-group { display: table; } .fileinput.input-group > * { position: relative; z-index: 2; } .fileinput.input-group > .btn-file { z-index: 1; } .fileinput-new.input-group .btn-file, .fileinput-new .input-group .btn-file { border-radius: 0 2px 2px 0; } .fileinput-new.input-group .btn-file.btn-xs, .fileinput-new .input-group .btn-file.btn-xs, .fileinput-new.input-group .btn-file.btn-sm, .fileinput-new .input-group .btn-file.btn-sm { border-radius: 0 2px 2px 0; } .fileinput-new.input-group .btn-file.btn-lg, .fileinput-new .input-group .btn-file.btn-lg { border-radius: 0 2px 2px 0; } .form-group.has-warning .fileinput .fileinput-preview { color: #ffa829; } .form-group.has-warning .fileinput .thumbnail { border-color: #ff760f; } .form-group.has-error .fileinput .fileinput-preview { color: #f6675d; } .form-group.has-error .fileinput .thumbnail { border-color: #f54556; } .form-group.has-success .fileinput .fileinput-preview { color: #67bd6a; } .form-group.has-success .fileinput .thumbnail { border-color: #61b555; } .input-group-addon:not(:first-child) { border-left: 0; } /*! * Waves v0.7.4 * http://fian.my.id/Waves * * Copyright 2014 Alfiana E. Sibuea and other contributors * Released under the MIT license * https://github.com/fians/Waves/blob/master/LICENSE */ .waves-effect { position: relative; cursor: pointer; display: inline-block; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; } .waves-effect .waves-ripple { position: absolute; border-radius: 50%; width: 100px; height: 100px; margin-top: -50px; margin-left: -50px; opacity: 0; background: rgba(0, 0, 0, 0.2); background: -webkit-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; -webkit-transition-property: -webkit-transform, opacity; -moz-transition-property: -moz-transform, opacity; -o-transition-property: -o-transform, opacity; transition-property: transform, opacity; -webkit-transform: scale(0) translate(0, 0); -moz-transform: scale(0) translate(0, 0); -ms-transform: scale(0) translate(0, 0); -o-transform: scale(0) translate(0, 0); transform: scale(0) translate(0, 0); pointer-events: none; } .waves-effect.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); background: -webkit-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); } .waves-effect.waves-classic .waves-ripple { background: rgba(0, 0, 0, 0.2); } .waves-effect.waves-classic.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); } .waves-notransition { -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important; } .waves-button, .waves-circle { -webkit-transform: translateZ(0); -moz-transform: translateZ(0); -ms-transform: translateZ(0); -o-transform: translateZ(0); transform: translateZ(0); -webkit-mask-image: -webkit-radial-gradient(circle, #ffffff 100%, #000000 100%); } .waves-button, .waves-button:hover, .waves-button:visited, .waves-button-input { white-space: nowrap; vertical-align: middle; cursor: pointer; border: none; outline: none; color: inherit; background-color: rgba(0, 0, 0, 0); font-size: 1em; line-height: 1em; text-align: center; text-decoration: none; z-index: 1; } .waves-button { padding: 0.85em 1.1em; border-radius: 0.2em; } .waves-button-input { margin: 0; padding: 0.85em 1.1em; } .waves-input-wrapper { border-radius: 0.2em; vertical-align: bottom; } .waves-input-wrapper.waves-button { padding: 0; } .waves-input-wrapper .waves-button-input { position: relative; top: 0; left: 0; z-index: 1; } .waves-circle { text-align: center; width: 2.5em; height: 2.5em; line-height: 2.5em; border-radius: 50%; } .waves-float { -webkit-mask-image: none; -webkit-box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); -webkit-transition: all 300ms; -moz-transition: all 300ms; -o-transition: all 300ms; transition: all 300ms; } .waves-float:active { -webkit-box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); } .waves-block { display: block; } /* Firefox Bug: link not triggered */ a.waves-effect .waves-ripple { z-index: -1; } /* * Load Website related LESS files */ /* * Generate Margin Class * margin, margin-top, margin-bottom, margin-left, margin-right */ .m-0 { margin: 0px !important; } .m-t-0 { margin-top: 0px !important; } .m-b-0 { margin-bottom: 0px !important; } .m-l-0 { margin-left: 0px !important; } .m-r-0 { margin-right: 0px !important; } .m-5 { margin: 5px !important; } .m-t-5 { margin-top: 5px !important; } .m-b-5 { margin-bottom: 5px !important; } .m-l-5 { margin-left: 5px !important; } .m-r-5 { margin-right: 5px !important; } .m-10 { margin: 10px !important; } .m-t-10 { margin-top: 10px !important; } .m-b-10 { margin-bottom: 10px !important; } .m-l-10 { margin-left: 10px !important; } .m-r-10 { margin-right: 10px !important; } .m-15 { margin: 15px !important; } .m-t-15 { margin-top: 15px !important; } .m-b-15 { margin-bottom: 15px !important; } .m-l-15 { margin-left: 15px !important; } .m-r-15 { margin-right: 15px !important; } .m-20 { margin: 20px !important; } .m-t-20 { margin-top: 20px !important; } .m-b-20 { margin-bottom: 20px !important; } .m-l-20 { margin-left: 20px !important; } .m-r-20 { margin-right: 20px !important; } .m-25 { margin: 25px !important; } .m-t-25 { margin-top: 25px !important; } .m-b-25 { margin-bottom: 25px !important; } .m-l-25 { margin-left: 25px !important; } .m-r-25 { margin-right: 25px !important; } .m-30 { margin: 30px !important; } .m-t-30 { margin-top: 30px !important; } .m-b-30 { margin-bottom: 30px !important; } .m-l-30 { margin-left: 30px !important; } .m-r-30 { margin-right: 30px !important; } /* * Generate Padding Class * padding, padding-top, padding-bottom, padding-left, padding-right */ .p-0 { padding: 0px !important; } .p-t-0 { padding-top: 0px !important; } .p-b-0 { padding-bottom: 0px !important; } .p-l-0 { padding-left: 0px !important; } .p-r-0 { padding-right: 0px !important; } .p-5 { padding: 5px !important; } .p-t-5 { padding-top: 5px !important; } .p-b-5 { padding-bottom: 5px !important; } .p-l-5 { padding-left: 5px !important; } .p-r-5 { padding-right: 5px !important; } .p-10 { padding: 10px !important; } .p-t-10 { padding-top: 10px !important; } .p-b-10 { padding-bottom: 10px !important; } .p-l-10 { padding-left: 10px !important; } .p-r-10 { padding-right: 10px !important; } .p-15 { padding: 15px !important; } .p-t-15 { padding-top: 15px !important; } .p-b-15 { padding-bottom: 15px !important; } .p-l-15 { padding-left: 15px !important; } .p-r-15 { padding-right: 15px !important; } .p-20 { padding: 20px !important; } .p-t-20 { padding-top: 20px !important; } .p-b-20 { padding-bottom: 20px !important; } .p-l-20 { padding-left: 20px !important; } .p-r-20 { padding-right: 20px !important; } .p-25 { padding: 25px !important; } .p-t-25 { padding-top: 25px !important; } .p-b-25 { padding-bottom: 25px !important; } .p-l-25 { padding-left: 25px !important; } .p-r-25 { padding-right: 25px !important; } .p-30 { padding: 30px !important; } .p-t-30 { padding-top: 30px !important; } .p-b-30 { padding-bottom: 30px !important; } .p-l-30 { padding-left: 30px !important; } .p-r-30 { padding-right: 30px !important; } /* * Generate Font-Size Classes (8px - 20px) */ .f-8 { font-size: 8px !important; } .f-9 { font-size: 9px !important; } .f-10 { font-size: 10px !important; } .f-11 { font-size: 11px !important; } .f-12 { font-size: 12px !important; } .f-13 { font-size: 13px !important; } .f-14 { font-size: 14px !important; } .f-15 { font-size: 15px !important; } .f-16 { font-size: 16px !important; } .f-17 { font-size: 17px !important; } .f-18 { font-size: 18px !important; } .f-19 { font-size: 19px !important; } .f-20 { font-size: 20px !important; } /* * Font Weight */ .f-300 { font-weight: 300 !important; } .f-400 { font-weight: 400 !important; } .f-500 { font-weight: 500 !important; } .f-700 { font-weight: 700 !important; } /* * Position Classes */ .p-relative { position: relative !important; } .p-absolute { position: absolute !important; } .p-fixed { position: fixed !important; } .p-static { position: static !important; } /* * Overflow */ .o-hidden { overflow: hidden !important; } .o-visible { overflow: visible !important; } .o-auto { overflow: auto !important; } /* * Display */ .d-block { display: block !important; } .di-block { display: inline-block !important; } /* * Material Background Colors */ .bgm-white { background-color: #ffffff !important; } .c-white { color: #ffffff !important; } .bgm-black { background-color: #000000 !important; } .c-black { color: #000000 !important; } .bgm-brown { background-color: #795548 !important; } .c-brown { color: #795548 !important; } .bgm-pink { background-color: #e91e63 !important; } .c-pink { color: #e91e63 !important; } .bgm-red { background-color: #f44336 !important; } .c-red { color: #f44336 !important; } .bgm-blue { background-color: #2196f3 !important; } .c-blue { color: #2196f3 !important; } .bgm-purple { background-color: #9c27b0 !important; } .c-purple { color: #9c27b0 !important; } .bgm-deeppurple { background-color: #673ab7 !important; } .c-deeppurple { color: #673ab7 !important; } .bgm-lightblue { background-color: #03a9f4 !important; } .c-lightblue { color: #03a9f4 !important; } .bgm-cyan { background-color: #00bcd4 !important; } .c-cyan { color: #00bcd4 !important; } .bgm-teal { background-color: #009688 !important; } .c-teal { color: #009688 !important; } .bgm-green { background-color: #4caf50 !important; } .c-green { color: #4caf50 !important; } .bgm-lightgreen { background-color: #8bc34a !important; } .c-lightgreen { color: #8bc34a !important; } .bgm-lime { background-color: #cddc39 !important; } .c-lime { color: #cddc39 !important; } .bgm-yellow { background-color: #ffeb3b !important; } .c-yellow { color: #ffeb3b !important; } .bgm-amber { background-color: #ffc107 !important; } .c-amber { color: #ffc107 !important; } .bgm-orange { background-color: #ff9800 !important; } .c-orange { color: #ff9800 !important; } .bgm-deeporange { background-color: #ff5722 !important; } .c-deeporange { color: #ff5722 !important; } .bgm-gray { background-color: #9e9e9e !important; } .c-gray { color: #9e9e9e !important; } .bgm-bluegray { background-color: #607d8b !important; } .c-bluegray { color: #607d8b !important; } .bgm-indigo { background-color: #3f51b5 !important; } .c-indigo { color: #3f51b5 !important; } /* * Background Colors */ .bg-black-trp { background-color: rgba(0, 0, 0, 0.1) !important; } /* * Border */ .b-0 { border: 0 !important; } /* * width */ .w-100 { width: 100% !important; } /* * Border Radius */ .brd-2 { border-radius: 2px; } /* * Media - Overriding the Media object to 3.2 version in order to prevent issues like text overflow. */ .media { overflow: visible; } .media:before, .media:after { content: " "; display: table; } .media:after { clear: both; } .media:before, .media:after { content: " "; display: table; } .media:after { clear: both; } .media > .pull-left { padding-right: 15px; } .media > .pull-right { padding-left: 15px; } .media-heading { font-size: 14px; margin-bottom: 10px; } .media-body { zoom: 1; display: block; width: auto; } .media-object { border-radius: 2px; } .close { opacity: 0.5; filter: alpha(opacity=50); font-weight: normal; text-shadow: none; } .close:hover { color: inherit; opacity: 1; filter: alpha(opacity=100); } .dl-horizontal dt { text-align: left; } *, button, input, i, a { -webkit-font-smoothing: antialiased; } *, *:active, *:hover { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } html { overflow-x: hidden\0/; -ms-overflow-style: none; } html, body { min-height: 100vh; } body { font-weight: 400; position: relative; } audio, video { outline: none; } p { margin-bottom: 20px; } small { font-size: 11px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small { font-size: 12px; } #main { position: relative; padding-bottom: 110px; padding-top: 65px; } .container.c-alt { max-width: 1170px; } @media (min-width: 768px) and (max-width: 1199px) { #content { padding-left: 15px; padding-right: 15px; } } @media (min-width: 1200px) { body.sw-toggled #content { padding-left: 220px; } } @media (min-width: 1200px) { body.sw-toggled #content > .container { width: calc(100% - 30px); } } .clist { list-style: none; } .clist > li:before { font-family: 'Material-Design-Iconic-Font'; margin: 0 10px 0 -20px; vertical-align: middle; } .clist.clist-angle > li:before { content: "\f2fb"; } .clist.clist-check > li:before { content: "\f26b"; } .clist.clist-star > li:before { content: "\f27d"; } /* * Common header classes & IDs * Do not remove this */ .header-inner { list-style: none; padding: 10px 0; margin-bottom: 0; position: relative; } .header-inner > li:not(.pull-right) { float: left; } .header-inner > li:not(:last-child) { margin-right: -2px; } .logo a { color: #fff; text-transform: uppercase; display: block; font-size: 16px; } #menu-trigger { width: 65px; height: 35px; cursor: pointer; } #menu-trigger .line-wrap .line { background-color: #fff; } #menu-trigger:before { content: ""; position: absolute; top: 13px; left: 7px; width: 45px; height: 45px; border-radius: 50%; background: rgba(255, 255, 255, 0.22); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); z-index: 0; } #menu-trigger.open:before { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .top-menu { list-style: none; padding: 0; } .top-menu > li { display: inline-block; margin: 0 1px; vertical-align: top; min-width: 50px; } @media (max-width: 767px) { .top-menu > li { position: static !important; } } .top-menu > li .dropdown-menu-lg { padding: 0; } .top-menu > li .dropdown-menu-lg .lv-body { min-height: 295px; overflow-x: hidden; } @media (min-width: 768px) { .top-menu > li:not(#toggle-width) { position: relative; } .top-menu > li:not(#toggle-width):before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.12); z-index: 0; border-radius: 2px; opacity: 0; filter: alpha(opacity=0); } .top-menu > li:not(#toggle-width):hover:before, .top-menu > li:not(#toggle-width).open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .top-menu > li > a { color: #fff; display: block; text-align: center; z-index: 1; position: relative; } .top-menu > li > a > .tm-icon { font-size: 24px; line-height: 36px; } .top-menu > li > a > .tm-label { line-height: 35px; white-space: nowrap; padding: 0 10px; font-size: 14px; text-transform: uppercase; } .top-menu > li > a > .tmn-counts { position: absolute; font-style: normal; background: #f44336; padding: 1px 5px; border-radius: 2px; right: 7px; top: -3px; font-size: 10px; line-height: 15px; } @media (max-width: 767px) { .top-menu .dropdown-menu-lg { width: calc(100% - 28px) !important; } .top-menu .dropdown-menu { right: 14px; top: 55px; } } #notifications { position: relative; } #notifications .lv-body { overflow-x: hidden; } #notifications:before { content: ""; position: absolute; width: 100%; height: calc(100% - 70px); background: url(../img/notifications.png) no-repeat center; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 400ms; transition-duration: 400ms; -webkit-transform: scale(0) rotate(-180deg); -ms-transform: scale(0) rotate(-180deg); -o-transform: scale(0) rotate(-180deg); transform: scale(0) rotate(-180deg); opacity: 0; filter: alpha(opacity=0); top: 42px; } #notifications.empty:before { -webkit-transform: scale(1) rotate(0deg); -ms-transform: scale(1) rotate(0deg); -o-transform: scale(1) rotate(0deg); transform: scale(1) rotate(0deg); opacity: 1; filter: alpha(opacity=100); } /* Full Screen */ :-webkit-full-screen [data-action="fullscreen"] { display: none; } :-moz-full-screen [data-action="fullscreen"] { display: none; } :-ms-fullscreen [data-action="fullscreen"] { display: none; } :full-screen [data-action="fullscreen"] { display: none; } :fullscreen [data-action="fullscreen"] { display: none; } /* ----------------------------- End common header classes and IDs------------------------------------- */ /* * For header type 1 only * You may remove these if you opt header 2 */ #header { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); min-height: 50px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; position: fixed; z-index: 11; width: 100%; left: 0; top: 0; padding: 0 11px; } #header:not(.sidebar-toggled).header-up { -webkit-transform: translate3d(0, -70px, 0); transform: translate3d(0, -70px, 0); } #header .logo a { padding: 7px 10px; } #top-search-wrap { position: absolute; top: -65px; left: 0; width: 100%; height: 70px; background: #fff; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; opacity: 0; filter: alpha(opacity=0); z-index: 10; } #top-search-wrap input[type="text"] { border: 0; height: 40px; padding: 0 10px 0 55px; font-size: 18px; width: 500px; border-radius: 2px; background-color: #efefef; width: 100%; } #top-search-wrap #top-search-close { position: absolute; top: 15px; font-size: 23px; font-style: normal; width: 45px; text-align: center; border-radius: 2px 0px 0px 2px; cursor: pointer; left: 15px; height: 40px; padding-top: 9px; } #top-search-wrap #top-search-close:hover { background-color: #e3e3e3; } @media (max-width: 767px) { #top-search-wrap #top-search-close { right: 7px; } } .tsw-inner { position: relative; padding: 15px; max-width: 700px; display: block; margin: 0 auto; } .search-toggled #top-search-wrap { top: 0; opacity: 1; filter: alpha(opacity=100); } /* Full Width Layout */ @media (min-width: 1200px) { #toggle-width .toggle-switch { margin: 9px 30px 0 0; } #toggle-width .toggle-switch .ts-helper { height: 11px; width: 33px; } #toggle-width .toggle-switch .ts-helper:before { width: 20px; height: 20px; top: -5px; } #toggle-width .toggle-switch input:checked + .ts-helper { background: rgba(0, 0, 0, 0.26); } #toggle-width .toggle-switch input:checked + .ts-helper:before { left: 18px; background: #fff; } } @media (max-width: 1200px) { #toggle-width { display: none; } } @media (min-width: 1200px) { .sw-toggled #header { padding-left: 15px; } .sw-toggled #menu-trigger { display: none; } } /* For Stupid IE9 */ .ie9 #header:not(.sidebar-toggled).header-up { display: none; } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For Header type 2 only * You may remove these if you opt header 1 */ #header-2 { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); position: relative; margin-bottom: -70px; z-index: 10; } @media (min-width: 768px) { #header-2 { padding: 15px 30px 0; } #header-2:before { content: ""; position: absolute; bottom: 0; left: 0; background: rgba(0, 0, 0, 0.04); width: 100%; height: 49px; } } #header-2 .search { margin-bottom: 25px; } @media (max-width: 767px) { #header-2 .search { padding: 0 20px; } } #header-2 .search input[type="text"] { width: 100%; background: transparent; border: 0; border-bottom: 1px solid rgba(255, 255, 255, 0.24); color: #fff; font-size: 15px; font-weight: 300; padding: 6px 0 6px 30px; } #header-2 .search input[type="text"]::-moz-placeholder { color: #ffffff; opacity: 1; } #header-2 .search input[type="text"]:-ms-input-placeholder { color: #ffffff; } #header-2 .search input[type="text"]::-webkit-input-placeholder { color: #ffffff; } #header-2 .search:after { background: #ffeb3b; } #header-2 .search .fg-line { max-width: 500px; position: relative; } #header-2 .search .fg-line:after { background: #ffeb3b; } #header-2 .search .fg-line:before { content: '\f1c3'; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 0; bottom: 1px; color: #fff; font-size: 22px; } .ha-menu > ul { list-style: none; padding: 0; margin: 0; } .ha-menu > ul > li { display: inline-block; vertical-align: top; } @media (max-width: 767px) { .ha-menu > ul > li { display: block; } } .ha-menu > ul > li:not(.active) > *:not(ul) { color: rgba(255, 255, 255, 0.6); } .ha-menu > ul > li.active > *:not(ul) { color: #fff; box-shadow: inset 0px -3px 0 0px #ffeb3b; } @media (max-width: 767px) { .ha-menu > ul > li.active > *:not(ul) { display: block; } } .ha-menu > ul > li > *:not(ul) { text-transform: uppercase; padding: 15px 12px; display: block; } .ha-menu > ul > li.open > *:not(ul), .ha-menu > ul > li > *:not(ul):hover { color: #fff; } .ha-menu > ul > li .dropdown-menu { margin-top: -5px; min-width: 100%; } @media (max-width: 767px) { .ha-menu { width: 200px; position: absolute; top: 65px; left: 8px; box-shadow: 0 0 10px; z-index: 10; padding: 0 10px; } .ha-menu:not(.toggled) { display: none; } } .sidebar { position: fixed; background: #fff; box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); height: calc(100% - 50px); top: 50px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; z-index: 10; opacity: 0; filter: alpha(opacity=0); overflow-y: auto; } .sidebar.toggled { opacity: 1; filter: alpha(opacity=100); } #sidebar { width: 220px; -webkit-transform: translate3d(-220px, 0, 0); transform: translate3d(-220px, 0, 0); } #sidebar.toggled { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .profile-menu > a { display: block; height: 96px; margin-bottom: 5px; width: 100%; background: url(../img/profile-menu.png) no-repeat left top; background-size: 100%; } .profile-menu > a .profile-pic { padding: 5px; } .profile-menu > a .profile-pic > img { width: 47px; height: 47px; border-radius: 50%; border: 3px solid rgba(0, 0, 0, 0.14); box-sizing: content-box; } .profile-menu > a .profile-info { background: rgba(0, 0, 0, 0.37); padding: 7px 14px; color: #fff; margin-top: 0px; position: relative; } .profile-menu > a .profile-info > i { font-size: 19px; line-height: 100%; position: absolute; right: 15px; top: 7px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-menu .main-menu { display: none; margin: 0 0 0; border-bottom: 1px solid #E6E6E6; } .profile-menu.toggled .profile-info > i { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); } .main-menu { list-style: none; padding-left: 0; margin: 20px 0 0 0; } .main-menu > li > a { padding: 14px 20px 14px 52px; display: block; color: #4C4C4C; font-weight: 500; position: relative; } .main-menu > li > a:hover { color: #262626; background-color: #f7f7f7; } .main-menu > li > a > i { position: absolute; left: 16px; font-size: 20px; top: 0; width: 25px; text-align: center; padding: 13px 0; } .main-menu > li.active > a { color: #262626; background-color: #F4F4F4; } .sub-menu > a { position: relative; } .sub-menu > a:before, .sub-menu > a:after { position: absolute; top: 12px; color: #575757; font-family: 'Material-Design-Iconic-Font'; font-size: 17px; right: 15px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .sub-menu > a:before { content: "\f278"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .sub-menu > a:after { content: "\f273"; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .sub-menu .sub-menu > a:before, .sub-menu .sub-menu > a:after { top: 5px; } .sub-menu.toggled > a:before { content: "\f278"; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .sub-menu.toggled > a:after { content: "\f273"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .sub-menu ul { list-style: none; display: none; padding: 0; } .sub-menu ul > li > a { color: #7f7f7f; padding: 8px 20px 8px 50px; font-weight: 500; display: block; } .sub-menu ul > li > a.active, .sub-menu ul > li > a:hover { color: #2196f3; } .sub-menu ul > li:first-child > a { padding-top: 14px; } .sub-menu ul > li:last-child > a { padding-bottom: 16px; } .sub-menu ul > li ul { font-size: 12px; margin: 10px 0; background-color: #f7f7f7; } .sub-menu.active > ul { display: block; } /* * layout */ body:not(.sw-toggled) #sidebar { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } @media (min-width: 1200px) { body.sw-toggled #sidebar { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; filter: alpha(opacity=100); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } @media (max-width: 1199px) { body.sw-toggled #sidebar { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } } /* * For Stupid IE9 */ @media (min-width: 1200px) { .ie9 body.sw-toggled #sidebar { display: block; } } .ie9 body:not(.sw-toggled) #sidebar:not(.toggled) { display: none; } .dropdown-menu { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .dropdown-menu > li > a { padding: 8px 17px; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .dropdown-menu.dropdown-menu-lg { width: 300px; } .dropdown-menu.dropdown-menu-sm { width: 150px; } .dropdown-menu.dropdown-menu-right { right: 0; left: auto; } .dropdown-menu.dropdown-menu-right > li > a { text-align: right; } .dropdown-menu.dm-icon > li > a > .zmdi { line-height: 100%; vertical-align: top; font-size: 18px; width: 28px; } .dropdown-menu:not([class*="bgm-"]) > li > a { color: #4C4C4C; } .dropdown-menu:not([class*="bgm-"]) > li > a:hover { color: #000; } .dropdown-menu[class*="bgm-"] > li > a { font-weight: 300; color: #fff; } .dropdown:not([data-animation]) .dropdown-menu, .btn-group:not([data-animation]) .dropdown-menu { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); display: block; } .dropdown .dropdown-menu:not([data-animation]).pull-right, .bootstrap-select .dropdown-menu:not([data-animation]).pull-right, .btn-group .dropdown-menu:not([data-animation]).pull-right, .dropdown .dropdown-menu:not([data-animation]).dropdown-menu-right, .bootstrap-select .dropdown-menu:not([data-animation]).dropdown-menu-right, .btn-group .dropdown-menu:not([data-animation]).dropdown-menu-right { -webkit-transform-origin: top right; -moz-transform-origin: top right; -ms-transform-origin: top right; transform-origin: top right; } .dropdown .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right), .bootstrap-select .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right), .btn-group .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right) { -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; } .dropup .dropdown-menu:not([data-animation]).pull-right, .dropup .dropdown-menu:not([data-animation]).dropdown-menu-right { -webkit-transform-origin: bottom right; -moz-transform-origin: bottom right; -ms-transform-origin: bottom right; transform-origin: bottom right; } .dropup .dropdown-menu:not([data-animation]):not(.pull-right):not(.dropdown-menu-right) { -webkit-transform-origin: bottom left; -moz-transform-origin: bottom left; -ms-transform-origin: bottom left; transform-origin: bottom left; } .dropdown.open .dropdown-menu:not([data-animation]), .dropup.open .dropdown-menu:not([data-animation]), .bootstrap-select.open .dropdown-menu:not([data-animation]), .btn-group.open .dropdown-menu:not([data-animation]) { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .dropdown-header { padding: 3px 17px; margin-top: 10px; color: #b1b1b1; text-transform: uppercase; font-weight: normal; } .btn-group.open .dropdown-toggle { box-shadow: none; } .listview { position: relative; } .listview:not(.lv-lg):not(.lv-message) .lv-item { padding: 10px 20px; } @media (min-width: 480px) { .listview.lv-lg .lv-item { padding: 17px 35px 17px 25px; } } @media (max-width: 767px) { .listview.lv-lg .lv-item { padding: 17px 35px 17px 20px; } } .listview.lv-lg .lv-item:hover { background-color: #FFFFDB; } .listview .lv-item { position: relative; display: block; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .listview .lv-item .lv-small { font-size: 12px; color: #A9A9A9; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; width: 100%; } .listview .lv-item .checkbox, .listview .lv-item.media { margin: 0; } .listview .lv-item .lv-actions { position: absolute; right: 15px; top: 10px; } @media (max-width: 480px) { .listview .lv-item .lv-actions { right: 7px; } } .listview .lv-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; } .listview a.lv-item:hover { background: #ECF9FF; } .listview [class*="lv-img"] { border-radius: 50%; } .listview .lv-img { width: 48px; height: 48px; } .listview .lv-img-sm { width: 35px; height: 35px; } .listview.lv-bordered .lv-item:not(:last-child) { border-bottom: 1px solid #f0f0f0; } .listview .lv-attrs { list-style: none; padding: 0; margin: 5px 0 0 0; } .listview .lv-attrs > li { display: inline-block; padding: 2px 10px 3px; font-size: 12px; margin-top: 5px; margin-right: 2px; } .listview .lv-attrs > li:not(.info):not(.primary):not(.warning):not(.danger) { border: 1px solid #dedede; background: #ffffff; color: #5e5e5e; } .listview .lv-attrs > li.info { border: 1px solid #00bcd4; background: #00bcd4; color: #ffffff; } .listview .lv-attrs > li.primary { border: 1px solid #2196f3; background: #2196f3; color: #ffffff; } .listview .lv-attrs > li.warning { border: 1px solid #ff9800; background: #ff9800; color: #ffffff; } .listview .lv-attrs > li.danger { border: 1px solid #f44336; background: #f44336; color: #ffffff; } .listview .lv-attrs > li > a { display: block; } .listview:not(.lv-message) .lv-title { color: #000; } [class*="lv-img"] { border-radius: 50%; } .lv-img { width: 48px; height: 48px; } .lv-img-sm { width: 35px; height: 35px; } .lv-header { text-align: center; padding: 15px 10px 13px; line-height: 100%; text-transform: uppercase; border-bottom: 1px solid #F0F0F0; font-weight: 500; color: #4C4C4C; margin-bottom: 10px; } .lv-header .actions { position: absolute; top: 6px; right: 8px; z-index: 10; } .lvh-search { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 4; background: #fff; display: none; } .lvh-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 24px; top: 17px; font-size: 22px; } .lvhs-input { border: 0; padding: 0 26px 0 55px; height: 63px; font-size: 18px; width: 100%; font-weight: 100; background: #fff; border-bottom: 1px solid #EEE; } .lvh-search-close { font-style: normal; position: absolute; top: 23px; right: 22px; font-size: 17px; width: 18px; height: 18px; background-color: #ADADAD; line-height: 100%; color: #fff; text-align: center; cursor: pointer; border-radius: 50%; } .lvh-search-close:hover { background: #333; } .lv-header-alt { position: relative; background: #f8f8f8; padding: 15px; } .lv-header-alt .lv-actions { z-index: 3; float: right; margin-top: 3px; position: relative; } .lv-header-alt .lv-actions > li > a { margin: 0 3px; } .lvh-label { color: #818181; display: inline-block; margin: 0; font-size: 14px; font-weight: normal; padding: 0 6px; line-height: 33px; vertical-align: middle; float: left; } .lv-footer { display: block; text-align: center; padding: 7px 10px 8px; border-top: 1px solid #F0F0F0; line-height: 100%; font-size: 11px; margin-top: 20px; color: #828282; } a.lv-footer:hover { color: #050505; } /* * Inside Card will have more padding */ .card-body .lv-item { padding: 12px 20px; } .progress { box-shadow: none; border-radius: 0; height: 5px; margin-bottom: 0; } .progress .progress-bar { box-shadow: none; } #chat { padding: 20px 0; width: 280px; right: -300px; } #chat.toggled { right: 0; } #chat .chat-search { padding: 20px 20px 15px 20px; } #chat .chat-search .form-control { background-image: url("../img/icons/search-2.png"); background-repeat: no-repeat; background-position: left center; padding-left: 30px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #chat .chat-search .form-control { background-image: url("../img/icons/search-2@2x.png"); background-size: 24px 24px; } } #chat .chat-search .form-control:focus { background-position: right center; padding: 0 30px 0 0; } /* * Chat Status Icons */ [class*="chat-status"] { position: absolute; width: 10px; height: 10px; border-radius: 50%; top: -3px; right: 12px; border: 2px solid #FFF; } /* Simple Mixin */ .chat-status-online { box-shadow: 0 0 0 1px #1ec01e; background: #1ec01e; } .chat-status-offline { box-shadow: 0 0 0 1px #e73f3f; background: #e73f3f; } .chat-status-busy { box-shadow: 0 0 0 1px #ffa500; background: #ffa500; } /* * For Stupid IE9 */ .ie9 #chat { right: 0; } .ie9 #chat:not(.toggled) { display: none; } .tab-nav { list-style: none; padding: 0; white-space: nowrap; margin: 0; overflow: auto; box-shadow: inset 0 -2px 0 0 #eeeeee; width: 100%; } .tab-nav li { display: inline-block; vertical-align: top; } .tab-nav li > a { display: inline-block; color: #7a7a7a; text-transform: uppercase; position: relative; width: 100%; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; font-weight: 500; } .tab-nav li > a:after { content: ""; height: 2px; position: absolute; width: 100%; left: 0; bottom: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } @media (min-width: 768px) { .tab-nav li > a { padding: 15px; } } @media (max-width: 768px) { .tab-nav li > a { padding: 15px 8px; } } .tab-nav li.active > a { color: #000; } .tab-nav li.active > a:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .tab-nav.tab-nav-right { text-align: right; } .tab-nav.tn-justified > li { display: table-cell; width: 1%; text-align: center; } .tab-nav.tn-icon > li .zmdi { font-size: 22px; line-height: 100%; min-height: 25px; } .tab-nav:not([data-tab-color]) > li > a:after { background: #2196f3; } .tab-nav[data-tab-color="green"] > li > a:after { background: #4caf50; } .tab-nav[data-tab-color="red"] > li > a:after { background: #f44336; } .tab-nav[data-tab-color="teal"] > li > a:after { background: #009688; } .tab-nav[data-tab-color="amber"] > li > a:after { background: #ffc107; } .tab-nav[data-tab-color="black"] > li > a:after { background: #000000; } .tab-nav[data-tab-color="cyan"] > li > a:after { background: #00bcd4; } .tab-content { padding: 20px 0; } .card { position: relative; background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); margin-bottom: 30px; } .card .card-header { position: relative; } @media screen and (min-width: 768px) { .card .card-header:not(.ch-alt) { padding: 23px 25px; } } @media screen and (max-width: 991px) { .card .card-header:not(.ch-alt) { padding: 18px; } } .card .card-header h2 { margin: 0; line-height: 100%; font-size: 17px; font-weight: 400; } .card .card-header h2 small { display: block; margin-top: 8px; color: #AEAEAE; line-height: 160%; } @media screen and (min-width: 768px) { .card .card-header.ch-alt { padding: 23px 26px; } } @media screen and (max-width: 991px) { .card .card-header.ch-alt { padding: 18px 18px 28px; } } .card .card-header.ch-alt:not([class*="bgm-"]) { background-color: #f7f7f7; } .card .card-header[class*="bgm-"] h2, .card .card-header[class*="bgm-"] h2 small { color: #fff; } .card .card-header .actions { position: absolute; right: 10px; z-index: 2; top: 15px; } .card .card-header .btn-float { right: 25px; bottom: -23px; z-index: 1; } @media screen and (min-width: 768px) { .card .card-body.card-padding { padding: 23px 26px; } } @media screen and (max-width: 991px) { .card .card-body.card-padding { padding: 18px; } } .card .card-body.card-padding-sm { padding: 15px; } .card-header:not(.ch-alt):not([class*="bgm-"]) + .card-padding { padding-top: 0; } .chart-edge { margin: 20px -8px 0 -10px; overflow: hidden; } .chart-edge .flot-chart { bottom: -14px; } .charts-row { margin-top: 50px; margin-bottom: 20px; } .mini-charts-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; } .mini-charts-item .chart { padding: 15px; float: left; } .mini-charts-item .chart.chart-pie { margin: 0 20px; } .mini-charts-item .count { overflow: hidden; color: rgba(255, 255, 255, 0.9); padding: 16px 12px; } .mini-charts-item .count > h2 { margin: 0; line-height: 100%; font-size: 22px; font-weight: 300; color: #fff; } .mini-charts-item .count > small { margin-bottom: 2px; display: block; } .mini-charts-item .count > h2, .mini-charts-item .count > small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .mini-charts-item > .clearfix, .mini-charts-item > .dl-horizontal dd, .mini-charts-item > .container, .mini-charts-item > .container-fluid, .mini-charts-item > .row, .mini-charts-item > .form-horizontal .form-group, .mini-charts-item > .btn-toolbar, .mini-charts-item > .btn-group-vertical > .btn-group, .mini-charts-item > .nav, .mini-charts-item > .navbar, .mini-charts-item > .navbar-header, .mini-charts-item > .navbar-collapse, .mini-charts-item > .pager, .mini-charts-item > .panel-body, .mini-charts-item > .modal-header, .mini-charts-item > .modal-footer { position: relative; z-index: 1; } .mini-charts-item:before { -webkit-transition: width; -o-transition: width; transition: width; -webkit-transition-duration: 500ms; transition-duration: 500ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; content: ""; width: 113px; height: 100%; background: rgba(0, 0, 0, 0.1); position: absolute; left: 0; top: 0; } .mini-charts-item:hover .count { color: #fff !important; } .mini-charts-item:hover:before { width: 100%; } /* * Sparkline Tooltip */ #jqstooltip { min-width: 21px; min-height: 23px; text-align: center; border: 0; background: #fff; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); background-color: #fff; } #jqstooltip .jqsfield { font-size: 12px; font-weight: 700; font-family: inherit; text-align: center; color: #333; } #jqstooltip .jqsfield > span { display: none; } /* * Easy Pie Charts */ .epc-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; padding: 30px 20px; text-align: center; } .easy-pie { display: inline-block; position: relative; padding: 0 5px 10px; } .easy-pie .percent { position: absolute; font-weight: 300; width: 100%; line-height: 100%; left: 0; } .easy-pie .percent:after { content: "%"; } .easy-pie.main-pie .percent { margin-top: 49px; font-size: 50px; text-align: center; } .easy-pie.main-pie .percent:not([class*="c-"]) { color: rgba(255, 255, 255, 0.7); } .easy-pie.main-pie .percent:after { font-size: 30px; } .easy-pie.main-pie .pie-title { color: #fff; } .easy-pie:not(.main-pie) .percent { font-size: 26px; margin-top: 37px; } .easy-pie:not(.main-pie) .percent:after { font-size: 20px; } .easy-pie .pie-title { position: absolute; width: 100%; text-align: center; bottom: -3px; left: 0; } /* * Recet Items Table Chart */ #recent-items-chart { width: calc(100% + 19px); height: 150px; margin: -20px -10px 0; bottom: -10px; } /* * Flot Chart */ [class*="flot-chart"] { width: 100%; display: block; } .flot-chart { height: 200px; } .flot-chart-pie { height: 300px; } @media (min-width: 768px) { .flot-chart-pie { margin-bottom: 20px; } } .flot-tooltip, #flotTip { position: absolute; color: #333; display: none; font-size: 12px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); padding: 3px 10px; background-color: #fff; z-index: 99999; } [class*="flc-"] { text-align: center; margin: 10px 0 5px; } [class*="flc-"] table { display: inline-block; } [class*="flc-"] .legendColorBox > div { border: #fff !important; } [class*="flc-"] .legendColorBox > div > div { border-radius: 50%; } [class*="flc-"] .legendLabel { padding: 0 8px 0 3px; } .dash-widget-item { position: relative; min-height: 380px; margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } .dash-widget-item .dash-widget-header { position: relative; } .dash-widget-item .dash-widget-header .actions { display: none; position: absolute; right: 4px; top: 6px; } .dash-widget-item .dash-widget-footer { position: absolute; left: 0; bottom: 0; width: 100%; } .dash-widget-item .dash-widget-title { padding: 12px 20px; position: absolute; width: 100%; left: 0; font-weight: 300; } .dash-widget-item:hover .dash-widget-header .actions { display: block; } /* * Site Visits */ #site-visits { color: rgba(255, 255, 255, 0.9); } #site-visits .dash-widget-header { padding-bottom: 38px; background-color: rgba(0, 0, 0, 0.13); } #site-visits .dash-widget-title { bottom: 0; background: rgba(0, 0, 0, 0.15); color: rgba(255, 255, 255, 0.9); } #site-visits h3 { color: rgba(255, 255, 255, 0.9); } /* * Best Selling Item */ #best-selling { background-color: #fff; } #best-selling .dash-widget-header > img { width: 100%; height: 155px; } #best-selling .dash-widget-header .dash-widget-title { padding-bottom: 30px; top: 0; color: #fff; background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#99000000', endColorstr='#00000000', GradientType=0); } #best-selling .dash-widget-header .main-item { padding: 15px; color: #fff; position: absolute; bottom: 0; left: 0; width: 100%; background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#99000000', GradientType=0); } #best-selling .dash-widget-header .main-item > h2 { font-weight: 400; font-size: 20px; margin: 5px 0 0 0; line-height: 100%; color: #fff; } /* * Weather */ #weather-widget { color: #fff; padding: 20px 20px 0; } #weather-widget .weather-status { font-size: 40px; line-height: 100%; } #weather-widget .weather-icon { text-align: center; margin-top: 10px; height: 150px; background-repeat: no-repeat; background-position: center; /* Weather Icons */ } #weather-widget .weather-icon.wi-0 { background-image: url("../img/icons/weather/0.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-0 { background-image: url("../img/icons/weather/0@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-1 { background-image: url("../img/icons/weather/1.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-1 { background-image: url("../img/icons/weather/1@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-2 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-2 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-3 { background-image: url("../img/icons/weather/3.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-3 { background-image: url("../img/icons/weather/3@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-4 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-4 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-5 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-5 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-6 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-6 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-7 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-7 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-8 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-8 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-9 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-9 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-10 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-10 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-11 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-11 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-12 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-12 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-13 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-13 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-14 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-14 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-15 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-15 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-16 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-16 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-17 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-17 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-18 { background-image: url("../img/icons/weather/18.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-18 { background-image: url("../img/icons/weather/18@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-19 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-19 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-20 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-20 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-21 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-21 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-22 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-22 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-23 { background-image: url("../img/icons/weather/19.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-23 { background-image: url("../img/icons/weather/19@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-24 { background-image: url("../img/icons/weather/24.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-24 { background-image: url("../img/icons/weather/24@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-25 { background-image: url("../img/icons/weather/24.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-25 { background-image: url("../img/icons/weather/24@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-26 { background-image: url("../img/icons/weather/26.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-26 { background-image: url("../img/icons/weather/26@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-27 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-27 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-28 { background-image: url("../img/icons/weather/28.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-28 { background-image: url("../img/icons/weather/28@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-29 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-29 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-30 { background-image: url("../img/icons/weather/28.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-30 { background-image: url("../img/icons/weather/28@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-31 { background-image: url("../img/icons/weather/31.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-31 { background-image: url("../img/icons/weather/31@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-32 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-32 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-33 { background-image: url("../img/icons/weather/31.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-33 { background-image: url("../img/icons/weather/31@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-34 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-34 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-35 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-35 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-36 { background-image: url("../img/icons/weather/32.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-36 { background-image: url("../img/icons/weather/32@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-37 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-37 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-38 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-38 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-39 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-39 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-40 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-40 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-41 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-41 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-42 { background-image: url("../img/icons/weather/9.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-42 { background-image: url("../img/icons/weather/9@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-43 { background-image: url("../img/icons/weather/5.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-43 { background-image: url("../img/icons/weather/5@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-44 { background-image: url("../img/icons/weather/27.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-44 { background-image: url("../img/icons/weather/27@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-45 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-45 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-46 { background-image: url("../img/icons/weather/18.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-46 { background-image: url("../img/icons/weather/18@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-icon.wi-47 { background-image: url("../img/icons/weather/2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { #weather-widget .weather-icon.wi-47 { background-image: url("../img/icons/weather/2@2x.png"); background-size: 125px 125px; } } #weather-widget .weather-info { list-style: none; padding: 0; margin: 3px 0 0 0; } #weather-widget .weather-info > li { display: inline-block; border: 1px solid rgba(255, 255, 255, 0.39); padding: 2px 10px 3px; margin-right: 5px; } #weather-widget .weather-list { background: rgba(0, 0, 0, 0.08); padding: 5px 12px; font-size: 16px; height: 51px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #weather-widget .weather-list > span { margin-right: 7px; font-weight: 300; display: inline-block; line-height: 40px; vertical-align: top; } #weather-widget .weather-list > span.weather-list-icon { width: 35px; height: 35px; background-repeat: no-repeat; background-position: center; background-size: 30px 30px; } #weather-widget .weather-list > span.weather-list-icon.wi-0 { background-image: url('../img/icons/weather/0.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-1 { background-image: url('../img/icons/weather/1.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-2 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-3 { background-image: url('../img/icons/weather/3.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-4 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-5 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-6 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-7 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-8 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-9 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-10 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-11 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-12 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-13 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-14 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-15 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-16 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-17 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-18 { background-image: url('../img/icons/weather/18.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-19 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-20 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-21 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-22 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-23 { background-image: url('../img/icons/weather/19.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-24 { background-image: url('../img/icons/weather/24.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-25 { background-image: url('../img/icons/weather/24.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-26 { background-image: url('../img/icons/weather/26.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-27 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-28 { background-image: url('../img/icons/weather/28.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-29 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-30 { background-image: url('../img/icons/weather/28.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-31 { background-image: url('../img/icons/weather/31.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-32 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-33 { background-image: url('../img/icons/weather/31.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-34 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-35 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-36 { background-image: url('../img/icons/weather/32.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-37 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-38 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-39 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-40 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-41 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-42 { background-image: url('../img/icons/weather/9.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-43 { background-image: url('../img/icons/weather/5.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-44 { background-image: url('../img/icons/weather/27.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-45 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-46 { background-image: url('../img/icons/weather/18.png'); } #weather-widget .weather-list > span.weather-list-icon.wi-47 { background-image: url('../img/icons/weather/2.png'); } #weather-widget .weather-list > span > i { line-height: 100%; font-size: 39px; } /* * Pie Charts */ #pie-charts { background: #fff; } #pie-charts .dash-widget-header { color: rgba(255, 255, 255, 0.9); } /* * Blog Post */ .blog-post .bp-header { position: relative; } .blog-post .bp-header > img { width: 100%; } .blog-post .bp-header .bp-title { background: #3f51b5; width: 100%; padding: 20px; color: #FFF; display: block; } .blog-post .bp-header .bp-title > h2 { color: #FFF; font-weight: 400; margin: 0 0 2px; line-height: 100%; font-size: 21px; } /* * Profile View */ .profile-view { text-align: center; } .profile-view .pv-header { position: relative; height: 145px; width: 100%; background-image: url('../img/headers/sm/4.png'); background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } .profile-view .pv-header > .pv-main { border-radius: 50%; width: 130px; position: absolute; height: 130px; bottom: -50px; left: 50%; margin-left: -65px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-view .pv-body { margin-top: 70px; padding: 0 20px 20px; } .profile-view .pv-body > h2 { margin: 0; line-height: 100%; font-size: 20px; font-weight: 400; } .profile-view .pv-body > small { display: block; color: #8E8E8E; margin: 10px 0 15px; } .profile-view .pv-body .pv-contact, .profile-view .pv-body .pv-follow { padding: 0; list-style: none; } .profile-view .pv-body .pv-contact > li, .profile-view .pv-body .pv-follow > li { display: inline-block; } .profile-view .pv-body .pv-follow { margin: 20px -20px; padding: 10px; background-color: #F7F7F7; border-top: 1px solid #EEE; border-bottom: 1px solid #EEE; } .profile-view .pv-body .pv-follow > li { padding: 0 10px; } .profile-view .pv-body .pv-contact > li { margin: 0 5px; } .profile-view .pv-body .pv-contact > li > .zmdi { line-height: 100%; vertical-align: text-bottom; font-size: 22px; } .profile-view .pv-body .pv-follow-btn { padding: 7px 20px; background: #00bcd4; color: #FFF; border-radius: 3px; text-transform: uppercase; display: block; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .profile-view .pv-body .pv-follow-btn:hover { background: #00a5bb; } .profile-view:hover .pv-main { -webkit-transform: scale(1.2); -ms-transform: scale(1.2); -o-transform: scale(1.2); transform: scale(1.2); } /* * Picture List */ .picture-list .pl-body { padding: 2px; } .picture-list .pl-body [class*="col-"] { padding: 0; padding: 2px; } .picture-list .pl-body [class*="col-"] > a { display: block; } @media (min-width: 768px) { .picture-list .pl-body [class*="col-"] > a { position: relative; } .picture-list .pl-body [class*="col-"] > a:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.3); z-index: 0; border-radius: 0; opacity: 0; filter: alpha(opacity=0); } .picture-list .pl-body [class*="col-"] > a:hover:before, .picture-list .pl-body [class*="col-"] > a.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .picture-list .pl-body [class*="col-"] > a img { width: 100%; } .picture-list .pl-body:before, .picture-list .pl-body:after { content: " "; display: table; } .picture-list .pl-body:after { clear: both; } .picture-list .pl-body:before, .picture-list .pl-body:after { content: " "; display: table; } .picture-list .pl-body:after { clear: both; } /* * Social */ .go-social .card-body { padding: 0 15px 20px; } .go-social .card-body [class*="col-"] { padding: 12px; } .go-social .card-body [class*="col-"] img { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .go-social .card-body [class*="col-"]:hover img { -webkit-transform: scale(1.2); -ms-transform: scale(1.2); -o-transform: scale(1.2); transform: scale(1.2); } /* * Rating */ .rating-list { padding: 0 0 10px; } .rating-list .rl-star { margin-top: 10px; margin-bottom: 4px; } .rating-list .rl-star .zmdi { font-size: 20px; } .rating-list .rl-star .zmdi:not(.active) { color: #ccc; } .rating-list .rl-star .zmdi.active { color: #ff9800; } .rating-list .lv-item .media .zmdi-star { line-height: 100%; font-size: 22px; color: #FF9800; vertical-align: middle; position: relative; top: -2px; left: 6px; } .rating-list .lv-item .media .media-body { padding: 7px 10px 0 5px; } .table { background-color: #ffffff; margin-bottom: 0; } .table > thead > tr > th { background-color: #fff; vertical-align: middle; font-weight: 500; color: #333; border-width: 1px; text-transform: uppercase; } .table [class*="bg-"] > tr > th { color: #fff; border-bottom: 0; } .table [class*="bg-"] + tbody > tr > td { border-top: 0; } .table.table-inner { border: 0; } .table > thead > tr > th:first-child, .table > tbody > tr > th:first-child, .table > tfoot > tr > th:first-child, .table > thead > tr > td:first-child, .table > tbody > tr > td:first-child, .table > tfoot > tr > td:first-child { padding-left: 30px; } .table > thead > tr > th:last-child, .table > tbody > tr > th:last-child, .table > tfoot > tr > th:last-child, .table > thead > tr > td:last-child, .table > tbody > tr > td:last-child, .table > tfoot > tr > td:last-child { padding-right: 30px; } .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > tbody > tr.succes > td, .table > tfoot > tr.succes > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td { border: 0; } .table > tbody > tr:last-child > td, .table > tfoot > tr:last-child > td { padding-bottom: 20px; } .table-striped td, .table-striped th { border: 0 !important; } .table-bordered { border-bottom: 0; border-left: 0; border-right: 0; } .table-bordered > tbody > tr > td, .table-bordered > tbody > tr > th { border-bottom: 0; border-left: 0; } .table-bordered > tbody > tr > td:last-child, .table-bordered > tbody > tr > th:last-child { border-right: 0; } .table-bordered > thead > tr > th { border-left: 0; } .table-bordered > thead > tr > th:last-child { border-right: 0; } .table-vmiddle td { vertical-align: middle !important; } .table-responsive { border: 0; } #todo-lists { background: #ffc107; color: #fff; margin-bottom: 30px; font-family: 'shadowsintolight', cursive; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } .tl-header { position: relative; padding: 25px; } .tl-header > h2 { margin: 0; color: #fff; line-height: 100%; } .tl-header > small { font-size: 17px; display: block; margin-top: 3px; } .tl-header .actions { position: absolute; right: 10px; padding: 0; list-style: none; top: 15px; } .tl-header .actions > li { display: inline-block; vertical-align: baseline; } .tl-body { min-height: 300px; position: relative; padding: 20px 10px 20px 25px; background: rgba(0, 0, 0, 0.03); } .tl-body .media-body { padding-top: 3px; font-size: 18px; } .tl-body .checkbox { margin-bottom: 15px; } .tl-body .checkbox span { display: inline-block; margin-top: -3px; } .tl-body .checkbox input:checked + i + span { text-decoration: line-through; } .tl-body .checkbox .input-helper:before { border-color: rgba(255, 255, 255, 0.8); border-width: 2px; } .tl-body .checkbox .input-helper:after { border-color: #fff; } #add-tl-item { width: 50px; height: 50px; border-radius: 50%; position: absolute; background: #fff; top: -25px; right: 23px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-transition-duration: 200ms; transition-duration: 200ms; } #add-tl-item .add-new-item { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } #add-tl-item .add-tl-body { overflow: hidden; opacity: 0; filter: alpha(opacity=0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } #add-tl-item .add-tl-body textarea { padding: 25px 25px 45px; resize: none; width: 100%; font-size: 24px; color: #ffc107; position: absolute; height: 100%; border: 0; outline: none; } #add-tl-item:not(.toggled) { overflow: hidden; } #add-tl-item:not(.toggled) .add-new-item { position: relative; z-index: 1; display: inline-block; width: 50px; height: 50px; background-repeat: no-repeat; background-position: center; cursor: pointer; text-align: center; font-size: 23px; color: #ff9800; line-height: 50px; } #add-tl-item.toggled { width: calc(100% - 47px); height: calc(100% - 25px); border-radius: 2px; top: 0; z-index: 1; box-shadow: 0 5px 8px rgba(0, 0, 0, 0.2); max-height: 300px; overflow: visible; } #add-tl-item.toggled .add-new-item { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); height: 0; overflow: hidden; float: left; } #add-tl-item.toggled .add-tl-body { opacity: 1; filter: alpha(opacity=100); } #add-tl-item.toggled .add-tl-body .add-tl-actions { position: absolute; bottom: 0; width: 100%; padding: 5px 10px; border-top: 1px solid #EEE; z-index: 1; } #add-tl-item.toggled .add-tl-body .add-tl-actions > a { font-size: 25px; padding: 0 6px; text-align: center; height: 40px; width: 40px; display: inline-block; line-height: 41px; border-radius: 50%; -webkit-transition: background-color; -o-transition: background-color; transition: background-color; -webkit-transition-duration: 300ms; transition-duration: 300ms; } #add-tl-item.toggled .add-tl-body .add-tl-actions > a:hover { background-color: #eee; } #add-tl-item.toggled .add-tl-body .add-tl-actions [data-tl-action="dismiss"] { color: #f44336; } #add-tl-item.toggled .add-tl-body .add-tl-actions [data-tl-action="save"] { color: #4caf50; } .btn { border: 0; text-transform: uppercase; } .btn[class*="bgm-"]:not(.bgm-white) { color: #fff; } .btn .caret { margin-top: -3px; } .btn:not(.btn-link) { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .btn-group:not(.bootstrap-select), .btn-group-vertical:not(.bootstrap-select) { box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.3); } .btn-group .btn, .btn-group-vertical .btn, .btn-group .btn:active, .btn-group-vertical .btn:active, .btn-group .btn:focus, .btn-group-vertical .btn:focus, .btn-group .btn-group, .btn-group-vertical .btn-group { box-shadow: none !important; } .btn-group .btn, .btn-group-vertical .btn { margin: 0; } .btn-xs, .btn-group-xs > .btn { padding: 2px 5px; font-size: 11px; line-height: 1.5; border-radius: 2px; } .btn-link { color: #797979; text-decoration: none; border-radius: 2px; } .btn-link:hover { color: #0a0a0a; } .btn-link:hover, .btn-link:active, .btn-link:focus { text-decoration: none; } .btn-inverse { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:focus, .btn-inverse.focus { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:hover { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { color: #ffffff; background-color: #2b2b2b; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active:hover, .btn-inverse.active:hover, .open > .dropdown-toggle.btn-inverse:hover, .btn-inverse:active:focus, .btn-inverse.active:focus, .open > .dropdown-toggle.btn-inverse:focus, .btn-inverse:active.focus, .btn-inverse.active.focus, .open > .dropdown-toggle.btn-inverse.focus { color: #ffffff; background-color: #1a1a1a; border-color: rgba(0, 0, 0, 0); } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { background-image: none; } .btn-inverse.disabled:hover, .btn-inverse[disabled]:hover, fieldset[disabled] .btn-inverse:hover, .btn-inverse.disabled:focus, .btn-inverse[disabled]:focus, fieldset[disabled] .btn-inverse:focus, .btn-inverse.disabled.focus, .btn-inverse[disabled].focus, fieldset[disabled] .btn-inverse.focus { background-color: #454545; border-color: transparent; } .btn-inverse .badge { color: #454545; background-color: #ffffff; } .btn-inverse:hover, .btn-inverse:focus, .btn-inverse.focus, .btn-inverse:active, .open > .dropdown-toggle.btn-inverse { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:hover:hover, .btn-inverse:focus:hover, .btn-inverse.focus:hover, .btn-inverse:active:hover, .open > .dropdown-toggle.btn-inverse:hover, .btn-inverse:hover:focus, .btn-inverse:focus:focus, .btn-inverse.focus:focus, .btn-inverse:active:focus, .open > .dropdown-toggle.btn-inverse:focus, .btn-inverse:hover.focus, .btn-inverse:focus.focus, .btn-inverse.focus.focus, .btn-inverse:active.focus, .open > .dropdown-toggle.btn-inverse.focus { color: #ffffff; background-color: #454545; border-color: transparent; } .btn-inverse:active, .btn-inverse.active, .open > .dropdown-toggle.btn-inverse { background-image: none; } .btn-inverse.disabled, .btn-inverse[disabled], fieldset[disabled] .btn-inverse, .btn-inverse.disabled:hover, .btn-inverse[disabled]:hover, fieldset[disabled] .btn-inverse:hover, .btn-inverse.disabled:focus, .btn-inverse[disabled]:focus, fieldset[disabled] .btn-inverse:focus, .btn-inverse.disabled.focus, .btn-inverse[disabled].focus, fieldset[disabled] .btn-inverse.focus, .btn-inverse.disabled:active, .btn-inverse[disabled]:active, fieldset[disabled] .btn-inverse:active { background-color: #454545; border-color: transparent; } .btn-inverse .badge { color: #454545; background-color: #ffffff; } .btn-icon { border-radius: 50%; width: 40px; line-height: 42px; height: 40px; padding: 0; text-align: center; } .btn-icon .zmdi { font-size: 17px; } .btn-icon-text > .zmdi { font-size: 15px; vertical-align: top; display: inline-block; margin-top: 2px; line-height: 100%; margin-right: 5px; } .btn-float { width: 50px; height: 50px; border-radius: 50%; line-height: 45px !important; } .btn-float:not(.m-btn) { position: absolute !important; } .btn-float i { font-size: 23px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 500ms; transition-duration: 500ms; } .btn-float:hover i { -webkit-transform: rotate(360deg); -ms-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } .btn-float:not(.bgm-white):not(.bgm-gray) > i { color: #fff; } .btn-float:not(.bgm-white):not(.bgm-gray) > i { color: #fff; } .btn-float.bgm-white > i, .btn-float.bgm-gray > i { color: #333; } .open .btn { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } .open .btn:focus, .open .btn:active { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } /* * Material Design Add button */ .m-btn { z-index: 1; bottom: 40px; right: 40px; position: fixed !important; } label { font-weight: 500; } /* * Reset Focus and Active shadows */ input:active, input:focus { outline: 0; box-shadow: none !important; } .form-control { box-shadow: none !important; resize: none; } .form-control:active, .form-control:focus { box-shadow: none; } .form-control:not(.fc-alt) { border-left: 0; border-right: 0; border-top: 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; padding: 0; } .form-control:not(.fc-alt).auto-size { padding-top: 6px; } /* * Checkbox and Radio */ .checkbox label, .radio label { padding-left: 30px; position: relative; } .checkbox input, .radio input { top: 0; left: 0; margin-left: 0 !important; z-index: 1; cursor: pointer; opacity: 0; filter: alpha(opacity=0); margin-top: 0; } .checkbox .input-helper:before, .radio .input-helper:before, .checkbox .input-helper:after, .radio .input-helper:after { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; position: absolute; content: ""; } .checkbox .input-helper:before, .radio .input-helper:before { left: 0; border: 1px solid #ccc; } .checkbox.disabled, .radio.disabled { opacity: 0.6; filter: alpha(opacity=60); } .checkbox input { width: 17px; height: 17px; } .checkbox input:checked + .input-helper:before { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .checkbox input:checked + .input-helper:after { -webkit-transform: scale(1) rotate(-50deg); -ms-transform: scale(1) rotate(-50deg); -o-transform: scale(1) rotate(-50deg); transform: scale(1) rotate(-50deg); opacity: 1; filter: alpha(opacity=100); } .checkbox .input-helper:before { top: 0; width: 17px; height: 17px; } .checkbox .input-helper:after { opacity: 0; filter: alpha(opacity=0); -webkit-transform: scale(0) rotate(80deg); -ms-transform: scale(0) rotate(80deg); -o-transform: scale(0) rotate(80deg); transform: scale(0) rotate(80deg); width: 22px; height: 9px; border-bottom: 2px solid #009688; border-left: 2px solid #009688; border-bottom-left-radius: 2px; left: -1px; top: 1px; } .radio input { width: 19px; height: 19px; } .radio input:checked + .input-helper:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .radio .input-helper:before { top: -1px; width: 19px; height: 19px; border-radius: 50%; } .radio .input-helper:after { width: 11px; height: 11px; background: #009688; border-radius: 50%; top: 3px; left: 4px; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .checkbox-inline, .radio-inline { vertical-align: top; margin-top: 0; padding-left: 25px; } /* * Select */ html:not(.ie9) .select { position: relative; } html:not(.ie9) .select:before { position: absolute; top: -1px; content: ""; height: calc(100% - 1px); width: 30px; background-color: #FFF; background-position: right calc(100% - 7px); background-repeat: no-repeat; background-image: url("../img/select.png"); pointer-events: none; z-index: 5; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { html:not(.ie9) .select:before { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } html:not(.ie9) .select:not(.fg-line):before { right: 0; } html:not(.ie9) .select.fg-line:before { right: 10px; } /* * Input Group Addon */ .input-group:not(.input-group-lg):not(.input-group-sm) .input-group-addon { font-size: 15px; } .input-group-addon { border-width: 0px 0px 1px 0px; min-width: 42px; } .input-group-addon > .zmdi { position: relative; top: 3px; } /* * Input Feilds */ .fg-line { position: relative; vertical-align: top; } .fg-line:not(.form-group) { display: inline-block; width: 100%; } .fg-line .form-control:disabled { color: #9d9d9d; background: transparent; } .fg-line:not(.disabled):after, .fg-line:not(.readonly):after { position: absolute; z-index: 3; bottom: 0; left: 0; height: 2px; width: 0; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .fg-line:not([class*=has-]):after { background: #2196f3; } .fg-line.readonly .form-control { color: #9d9d9d; background: transparent; } .fg-line.fg-toggled:after { width: 100%; } .fg-float { margin-top: 2px; position: relative; } .fg-float .form-control { position: relative; background: transparent; z-index: 1; } .fg-float .form-control::-moz-placeholder { color: #ffffff; opacity: 1; } .fg-float .form-control:-ms-input-placeholder { color: #ffffff; } .fg-float .form-control::-webkit-input-placeholder { color: #ffffff; } .fg-float .fg-label { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; position: absolute; top: 5px; font-weight: 400; color: #959595; pointer-events: none; z-index: 0; left: 0; white-space: nowrap; } .fg-float .fg-toggled .fg-label { top: -20px; font-size: 11px; } .control-label { font-weight: normal; } /* * Toggle Switch */ .toggle-switch { display: inline-block; vertical-align: top; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .toggle-switch .ts-label { display: inline-block; margin: 0 20px 0 0; vertical-align: top; -webkit-transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); } .toggle-switch .ts-helper { display: inline-block; position: relative; width: 40px; height: 16px; border-radius: 8px; background: rgba(0, 0, 0, 0.26); -webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); vertical-align: middle; cursor: pointer; } .toggle-switch .ts-helper:before { content: ''; position: absolute; top: -4px; left: -4px; width: 24px; height: 24px; background: #fafafa; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); border-radius: 50%; webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } .toggle-switch:not(.disabled) .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(128, 128, 128, 0.1); } .toggle-switch input { position: absolute; z-index: 1; width: 46px; margin: 0 0 0 -4px; height: 24px; opacity: 0; filter: alpha(opacity=0); cursor: pointer; } .toggle-switch input:checked + .ts-helper:before { left: 20px; } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper { background: rgba(0, 150, 136, 0.5); } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper:before { background: #009688; } .toggle-switch:not([data-ts-color]) input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(0, 150, 136, 0.2); } .toggle-switch.disabled { opacity: 0.6; filter: alpha(opacity=60); } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper { background: rgba(244, 67, 54, 0.5); } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper:before { background: #f44336; } .toggle-switch[data-ts-color="red"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(244, 67, 54, 0.2); } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper { background: rgba(33, 150, 243, 0.5); } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper:before { background: #2196f3; } .toggle-switch[data-ts-color="blue"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(33, 150, 243, 0.2); } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper { background: rgba(255, 193, 7, 0.5); } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper:before { background: #ffc107; } .toggle-switch[data-ts-color="amber"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(255, 193, 7, 0.2); } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper { background: rgba(156, 39, 176, 0.5); } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper:before { background: #9c27b0; } .toggle-switch[data-ts-color="purple"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(156, 39, 176, 0.2); } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper { background: rgba(233, 30, 99, 0.5); } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper:before { background: #e91e63; } .toggle-switch[data-ts-color="pink"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(233, 30, 99, 0.2); } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper { background: rgba(205, 220, 57, 0.5); } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper:before { background: #cddc39; } .toggle-switch[data-ts-color="lime"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(205, 220, 57, 0.2); } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper { background: rgba(0, 188, 212, 0.5); } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper:before { background: #00bcd4; } .toggle-switch[data-ts-color="cyan"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(0, 188, 212, 0.2); } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper { background: rgba(76, 175, 80, 0.5); } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper:before { background: #4caf50; } .toggle-switch[data-ts-color="green"] input:not(:disabled):checked + .ts-helper:active:before { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(76, 175, 80, 0.2); } /* * IE 9 Placeholder */ .ie9-placeholder { color: #888 !important; font-weight: normal; } /* * Validation */ .has-error .checkbox .input-helper:before { border-color: #f99d97; } .has-error .checkbox .input-helper:after { border-bottom-color: #f77066; border-left-color: #f77066; } .has-error .fg-line:after { background: #f44336; } .has-success .checkbox .input-helper:before { border-color: #92cf94; } .has-success .checkbox .input-helper:after { border-bottom-color: #6ec071; border-left-color: #6ec071; } .has-success .fg-line:after { background: #4caf50; } .has-warning .checkbox .input-helper:before { border-color: #ffc166; } .has-warning .checkbox .input-helper:after { border-bottom-color: #ffad33; border-left-color: #ffad33; } .has-warning .fg-line:after { background: #ff9800; } .pagination { border-radius: 0; } .pagination > li { margin: 0 2px; display: inline-block; vertical-align: top; } .pagination > li > a, .pagination > li > span { border-radius: 50% !important; padding: 0; width: 40px; height: 40px; line-height: 38px; text-align: center; font-size: 14px; z-index: 1; position: relative; cursor: pointer; } .pagination > li > a > .zmdi, .pagination > li > span > .zmdi { font-size: 22px; line-height: 39px; } .pagination > li.disabled { opacity: 0.5; filter: alpha(opacity=50); } /* * Listview Pagination */ .lv-pagination { width: 100%; text-align: center; padding: 40px 0; border-top: 1px solid #F0F0F0; margin-top: 0; margin-bottom: 0; } /* * Pager */ .pager li > a, .pager li > span { padding: 5px 10px 6px; color: #7e7e7e; } .popover { box-shadow: 0 2px 30px rgba(0, 0, 0, 0.2); } .popover-title { border-bottom: 0; padding: 15px; font-size: 12px; text-transform: uppercase; } .popover-title + .popover-content { padding-top: 0; } .popover-content { padding: 15px; } .popover-content p { margin-bottom: 0; } .fw-container .tab-content { padding: 25px 0; } .fw-container .fw-footer { text-align: center; margin: 30px 0 0; width: 100%; border-top: 2px solid #eee; padding: 15px 0; } .alert { padding-left: 30px; font-size: 13px; } .alert span { cursor: pointer; } .alert:not(.alert-dismissible) { padding-right: 30px; } .alert.alert-dismissable { padding-right: 44px; } .alert-inverse { background-color: #333333; border-color: transparent; color: #ffffff; } .alert-inverse hr { border-top-color: rgba(0, 0, 0, 0); } .alert-inverse .alert-link { color: #e6e6e6; } .growl-animated.alert-inverse { box-shadow: 0 0 5px rgba(51, 51, 51, 0.5); } .growl-animated.alert-info { box-shadow: 0 0 5px rgba(33, 150, 243, 0.5); } .growl-animated.alert-success { box-shadow: 0 0 5px rgba(76, 175, 80, 0.5); } .growl-animated.alert-warning { box-shadow: 0 0 5px rgba(255, 193, 7, 0.5); } .growl-animated.alert-danger { box-shadow: 0 0 5px rgba(244, 67, 54, 0.5); } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #e0e0e0; box-shadow: 0 0 6px #EAEAEA; } /* * Lightbox */ .lightbox .lightbox-item > img { width: 100%; border-radius: 2px; } @media (min-width: 768px) { .lightbox .lightbox-item { position: relative; } .lightbox .lightbox-item:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.1); z-index: 0; border-radius: 0; opacity: 0; filter: alpha(opacity=0); } .lightbox .lightbox-item:hover:before, .lightbox .lightbox-item.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .lightbox .lightbox-item:hover { cursor: pointer; } .lightbox [data-src]:before, .lightbox [data-src]:after { content: " "; display: table; } .lightbox [data-src]:after { clear: both; } .lightbox [data-src]:before, .lightbox [data-src]:after { content: " "; display: table; } .lightbox [data-src]:after { clear: both; } .lightbox .lightbox-item:not(.p-item) { position: relative; } /* * Carousel */ .carousel .carousel-control { -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; opacity: 0; filter: alpha(opacity=0); } .carousel .carousel-control .zmdi { position: absolute; top: 50%; left: 50%; line-height: 100%; } @media screen and (min-width: 768px) { .carousel .carousel-control .zmdi { font-size: 60px; width: 60px; height: 60px; margin-top: -30px; margin-left: -30px; } } @media screen and (max-width: 991px) { .carousel .carousel-control .zmdi { width: 24px; height: 24px; margin-top: -12px; margin-left: -12px; } } .carousel:hover .carousel-control { opacity: 1; filter: alpha(opacity=100); } .carousel .carousel-caption { background: rgba(0, 0, 0, 0.6); left: 0; right: 0; bottom: 0; width: 100%; padding-bottom: 50px; } .carousel .carousel-caption > h3 { color: #fff; margin: 0 0 5px; font-weight: 300; } .carousel .carousel-caption > p { margin: 0; } @media screen and (max-width: 991px) { .carousel .carousel-caption { display: none; } } .carousel .carousel-indicators { bottom: 10px; margin: 0; left: 0; bottom: 0; width: 100%; padding: 0 0 6px; background: rgba(0, 0, 0, 0.6); } .carousel .carousel-indicators li { border-radius: 0; width: 15px; border: 0; background: #fff; height: 3px; margin: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .carousel .carousel-indicators li.active { width: 25px; height: 3px; background: #ff9800; } .modal .modal-content { box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31); border-radius: 3px; border: 0; } .modal .modal-header { padding: 23px 26px; } .modal .modal-body { padding: 0 26px 10px; } .modal .modal-footer .btn-link { font-size: 14px; color: #000; font-weight: 500; } .modal .modal-footer .btn-link:hover { background-color: #eee; } .modal:not([data-modal-color]) .modal-footer .btn-link { font-weight: 500; } .modal:not([data-modal-color]) .modal-footer .btn-link:hover { background-color: #eee; } .modal[data-modal-color] { color: #fff; } .modal[data-modal-color] .modal-title, .modal[data-modal-color] .modal-footer .btn-link { color: #fff; } .modal[data-modal-color] .modal-footer { background: rgba(0, 0, 0, 0.1); } .modal[data-modal-color] .modal-backdrop { background: #fff; } .modal[data-modal-color] .modal-footer .btn-link { font-weight: 400; } .modal[data-modal-color] .modal-footer .btn-link:hover { background-color: rgba(0, 0, 0, 0.1); } .modal[data-modal-color="blue"] .modal-content { background: #2196f3; } .modal[data-modal-color="cyan"] .modal-content { background: #00bcd4; } .modal[data-modal-color="green"] .modal-content { background: #4caf50; } .modal[data-modal-color="lightgreen"] .modal-content { background: #8bc34a; } .modal[data-modal-color="lightblue"] .modal-content { background: #03a9f4; } .modal[data-modal-color="amber"] .modal-content { background: #ffc107; } .modal[data-modal-color="teal"] .modal-content { background: #009688; } .modal[data-modal-color="orange"] .modal-content { background: #ff9800; } .modal[data-modal-color="bluegray"] .modal-content { background: #607d8b; } .modal[data-modal-color="red"] .modal-content { background: #f44336; } .panel { box-shadow: none; border: 0; } .panel-heading { padding: 0; } .panel-title > a { padding: 10px 15px; display: block; font-size: 13px; } .panel-collapse .panel-heading { position: relative; } .panel-collapse .panel-heading .panel-title > a { padding: 8px 5px 16px 30px; color: #000; position: relative; } .panel-collapse .panel-heading .panel-title > a:after, .panel-collapse .panel-heading .panel-title > a:before { position: absolute; bottom: 0; left: 0; height: 2px; width: 100%; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .panel-collapse .panel-heading .panel-title > a:after { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .panel-collapse .panel-heading:not(.active) .panel-title > a:before { background: #eee; } .panel-collapse .panel-heading:before, .panel-collapse .panel-heading:after { font-family: 'Material-Design-Iconic-Font'; font-size: 17px; position: absolute; left: 0; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; top: 4px; } .panel-collapse .panel-heading:before { content: "\f278"; -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-heading:after { -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); content: "\f273"; } .panel-collapse .panel-heading.active .panel-title > a:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-heading.active:before { -webkit-transform: scale(0) rotate(-90deg); -ms-transform: scale(0) rotate(-90deg); -o-transform: scale(0) rotate(-90deg); transform: scale(0) rotate(-90deg); } .panel-collapse .panel-heading.active:after { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .panel-collapse .panel-body { border-top: 0 !important; padding-left: 5px; padding-right: 5px; } .panel-group:not([data-collapse-color]) .panel-collapse .panel-heading.active .panel-title > a:after { background: #2196f3; } .panel-group[data-collapse-color="red"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #f44336; } .panel-group[data-collapse-color="green"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #4caf50; } .panel-group[data-collapse-color="amber"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #ffc107; } .panel-group[data-collapse-color="teal"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #009688; } .panel-group[data-collapse-color="black"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #000000; } .panel-group[data-collapse-color="cyan"] .panel-collapse .panel-heading.active .panel-title > a:after { background: #00bcd4; } .tooltip-inner { border-radius: 1px; padding: 3px 10px 5px; } .breadcrumb { border-bottom: 1px solid #E5E5E5; border-radius: 0; } .breadcrumb > li > a { color: #A9A9A9; } .breadcrumb > li > a:hover { color: #7c7c7c; } @media (min-width: 768px) { body:not(.sw-toggled) .breadcrumb { padding: 10px 33px 11px; } } @media (min-width: 1199px) { body.sw-toggled .breadcrumb { padding: 10px 33px 11px 280px; } } #messages-main { position: relative; } #messages-main:before, #messages-main:after { content: " "; display: table; } #messages-main:after { clear: both; } #messages-main:before, #messages-main:after { content: " "; display: table; } #messages-main:after { clear: both; } #messages-main .ms-block { padding: 23px 20px 0; } #messages-main .ms-menu { position: absolute; left: 0; top: 0; background: #F8F8F8; border-right: 1px solid #EEE; padding-bottom: 50px; height: 100%; width: 240px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } @media (max-width: 767px) { #messages-main .ms-menu { height: calc(100% - 58px); -webkit-transform: translate3d(-240px, 58px, 0); transform: translate3d(-240px, 58px, 0); opacity: 0; filter: alpha(opacity=0); z-index: 1; } #messages-main .ms-menu.toggled { -webkit-transform: translate3d(0, 58px, 0); transform: translate3d(0, 58px, 0); opacity: 1; filter: alpha(opacity=100); } } #messages-main .ms-menu .lv-item { padding-left: 20px; padding-right: 20px; } #messages-main .ms-menu .lv-item.active { background: #fff; } #messages-main .ms-menu .lv-item:not(.active):hover { background: #F2F2F2; cursor: pointer; } @media (min-width: 768px) { #messages-main .ms-body { padding-left: 240px; } } @media (max-width: 767px) { #messages-main .ms-body { overflow: hidden; } } #messages-main .ms-user:before, #messages-main .ms-user:after { content: " "; display: table; } #messages-main .ms-user:after { clear: both; } #messages-main .ms-user:before, #messages-main .ms-user:after { content: " "; display: table; } #messages-main .ms-user:after { clear: both; } #messages-main .ms-user > img { border-radius: 50%; width: 40px; float: left; } #messages-main .ms-user > div { overflow: hidden; padding: 7px 5px 7px 15px; font-size: 11px; } #ms-menu-trigger { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; float: left; margin: 1px 0 0 -7px; } @media (min-width: 768px) { #ms-menu-trigger { display: none; } } #ms-menu-trigger .line-wrap .line { background-color: #717171; } /* * For Message */ .lv-message .lv-item { padding: 20px; } .lv-message .lv-item.right { text-align: right; } .lv-message .lv-item.right .lv-avatar { margin-right: 0; margin-left: 15px; } .lv-message .lv-item:not(.right) .ms-item { background: #ffc107; color: #fff; } .lv-message .lv-item.right .ms-item { background: #eee; } .lv-avatar { width: 35px; height: 35px; border-radius: 50%; color: #FFF; text-align: center; line-height: 34px; font-size: 15px; margin-right: 15px; padding: 0 !important; text-transform: uppercase; } .lv-avatar > img { width: 35px; height: 35px; border-radius: 50%; vertical-align: top; } .ms-item { padding: 13px 19px 15px; border-radius: 2px; display: inline-block; } @media (min-width: 768px) { .ms-item { max-width: 70%; } } .ms-date { display: block; color: #B3B3B3; margin-top: 7px; } .ms-date > i { font-size: 14px; vertical-align: bottom; line-height: 100%; } .ms-reply { box-shadow: 0 -20px 20px -5px #ffffff; position: relative; margin: 0 !important; } .ms-reply textarea { width: 100%; font-size: 13px; border: 0; padding: 10px 8px; resize: none; height: 60px; } .ms-reply button { position: absolute; top: 0; right: 0; border: 0; height: 100%; width: 60px; font-size: 25px; background: #F5F5F5; color: #2196f3; } .ms-reply button:hover { background: #f2f2f2; } .four-zero-content { background: #fff; padding: 20px; } .four-zero-content:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #EDECEC; content: ""; } .four-zero { background: #00bcd4; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; position: absolute; top: 50%; margin-top: -150px; color: #fff; text-align: center; padding: 15px; height: 300px; width: 500px; left: 50%; margin-left: -250px; } .four-zero h2 { font-size: 130px; } @media (max-width: 767px) { .four-zero { width: calc(100% - 40px); left: 20px; margin-left: 0; height: 260px; margin-top: -130px; } .four-zero h2 { font-size: 90px; } } .four-zero h2 { line-height: 100%; color: #fff; font-weight: 100; } .four-zero small { display: block; font-size: 26px; margin-top: -10px; } .four-zero footer { background: rgba(0, 0, 0, 0.13); position: absolute; left: 0; bottom: 0; width: 100%; padding: 10px; } .four-zero footer > a { font-size: 21px; display: inline-block; color: #FFF; margin: 0 1px; line-height: 40px; width: 40px; height: 40px; background: rgba(0, 0, 0, 0.09); border-radius: 50%; text-align: center; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .four-zero footer > a:hover { background: rgba(0, 0, 0, 0.2); } .login-content { overflow: hidden; height: 100%; } .lc-block { background: #fff; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; width: 500px; display: inline-block; margin-top: 42px; vertical-align: middle; position: relative; } .lc-block:not(.toggled) { display: none; } .lc-block.toggled { -webkit-animation-name: fadeInUp; animation-name: fadeInUp; -webkit-animation-duration: 300ms; animation-duration: 300ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; z-index: 10; } .lc-block:not(.lcb-alt) { padding: 35px 55px 35px; } @media (max-width: 767px) { .lc-block { padding: 15px 35px 25px 20px; width: calc(100% - 60px); } } .lc-block .checkbox { margin: 5px 0 0 42px; text-align: left; } .lc-block:not(.lcb-alt) .btn-login { top: 50%; margin-top: -25px; right: -25px; } .login-navigation { list-style: none; padding: 0; margin: 0; position: absolute; width: 100%; text-align: center; left: 0%; bottom: -45px; } .login-navigation > li { display: inline-block; margin: 0 2px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 150ms; transition-duration: 150ms; cursor: pointer; vertical-align: top; color: #fff; line-height: 16px; min-width: 16px; min-height: 16px; text-transform: uppercase; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; } .login-navigation > li > span { opacity: 0; filter: alpha(opacity=0); } .login-navigation > li:not(:hover) { font-size: 0px; border-radius: 100%; } .login-navigation > li:hover { border-radius: 10px; padding: 0 5px; font-size: 8px; } .login-navigation > li:hover > span { opacity: 1; filter: alpha(opacity=100); } .lcb-alt { padding: 70px 55px 60px; } .lcb-alt .btn-login { bottom: -25px; left: 50%; margin-left: -25px; } .lcb-alt .login-navigation { bottom: -75px; } .lcb-user { width: 100px; height: 100px; border-radius: 50%; border: 5px solid #fff; position: absolute; top: -50px; left: 50%; margin-left: -50px; box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.18); } body.login-content { text-align: center; } body.login-content:after { content: ""; vertical-align: middle; display: inline-block; width: 1px; height: 100%; } body.login-content:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #00bcd4; content: ""; z-index: 0; } #profile-main { min-height: 500px; position: relative; } #profile-main .pm-overview { overflow-y: auto; } @media (min-width: 1200px) { #profile-main .pm-overview { width: 300px; } } @media (min-width: 768px) and (max-width: 1200px) { #profile-main .pm-overview { width: 250px; } } @media (min-width: 768px) { #profile-main .pm-overview { position: absolute; left: 0; top: 0; height: 100%; background: #f8f8f8; border-right: 1px solid #eee; } } @media (max-width: 767px) { #profile-main .pm-overview { width: 100%; background: #333; text-align: center; } } @media (min-width: 1200px) { #profile-main .pm-body { padding-left: 300px; } } @media (min-width: 768px) and (max-width: 1200px) { #profile-main .pm-body { padding-left: 250px; } } @media (max-width: 767px) { #profile-main .pm-body { padding-left: 0; } } #profile-main .pmo-pic { position: relative; margin: 20px; } @media (min-width: 768px) { #profile-main .pmo-pic img { width: 100%; border-radius: 2px 2px 0 0; } } @media (max-width: 767px) { #profile-main .pmo-pic img { width: 180px; display: inline-block; height: 180px; border-radius: 50%; border: 4px solid #fff; box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } } #profile-main .pmo-pic .pmo-stat { border-radius: 0 0 2px 2px; color: #fff; text-align: center; padding: 30px 5px 0; } @media (min-width: 768px) { #profile-main .pmo-pic .pmo-stat { background: #ffc107; padding-bottom: 15px; } } #profile-main .pmo-pic .pmop-edit { position: absolute; top: 0; left: 0; color: #fff; background: rgba(0, 0, 0, 0.38); text-align: center; padding: 10px 10px 11px; -webkit-transition: opacity; -o-transition: opacity; transition: opacity; -webkit-transition-duration: 250ms; transition-duration: 250ms; } #profile-main .pmo-pic .pmop-edit:hover { background: rgba(0, 0, 0, 0.8); } #profile-main .pmo-pic .pmop-edit i { font-size: 18px; vertical-align: middle; margin-top: -3px; } @media (min-width: 768px) { #profile-main .pmo-pic .pmop-edit { width: 100%; opacity: 0; filter: alpha(opacity=0); } #profile-main .pmo-pic .pmop-edit i { margin-right: 4px; } } #profile-main .pmo-pic:hover .pmop-edit { opacity: 1; filter: alpha(opacity=100); } #profile-main .pmo-pic .pmop-message { position: absolute; bottom: 27px; left: 50%; margin-left: -25px; } #profile-main .pmo-pic .pmop-message .dropdown-menu { padding: 5px 0 55px; left: -90px; width: 228px; height: 150px; top: -74px; -webkit-transform-origin: center; -moz-transform-origin: center; -ms-transform-origin: center; transform-origin: center; } #profile-main .pmo-pic .pmop-message .dropdown-menu textarea { width: 100%; height: 95px; border: 0; resize: none; padding: 10px 19px; } #profile-main .pmo-pic .pmop-message .dropdown-menu button { bottom: 5px; left: 88px; } #profile-main .pmb-block { margin-bottom: 20px; } @media (min-width: 1200px) { #profile-main .pmb-block { padding: 40px 42px 0; } } @media (max-width: 1199px) { #profile-main .pmb-block { padding: 30px 20px 0; } } #profile-main .pmb-block:last-child { margin-bottom: 50px; } #profile-main .pmb-block .pmbb-header { margin-bottom: 25px; position: relative; } #profile-main .pmb-block .pmbb-header .actions { position: absolute; top: -2px; right: 0; } #profile-main .pmb-block .pmbb-header h2 { margin: 0; font-weight: 100; font-size: 20px; } #profile-main .pmb-block .pmbb-edit { position: relative; z-index: 1; display: none; } #profile-main .pmb-block .pmbb-edit, #profile-main .pmb-block .pmbb-view { -webkit-animation-name: fadeIn; animation-name: fadeIn; -webkit-animation-duration: 1000ms; animation-duration: 1000ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; } #profile-main .pmb-block.toggled .pmbb-edit { display: block; } #profile-main .pmb-block.toggled .pmbb-view { display: none; } #profile-main .pmo-block { padding: 25px; } #profile-main .pmo-block > h2 { font-size: 16px; margin: 0 0 15px; } #profile-main .pmo-items .pmob-body { padding: 0 10px; } #profile-main .pmo-items a { display: block; padding: 4px; } #profile-main .pmo-items a img { width: 100%; } .pmo-contact ul { list-style: none; margin: 0; padding: 0; } .pmo-contact ul li { position: relative; padding: 8px 0 8px 35px; } .pmo-contact ul li i { font-size: 18px; vertical-align: top; line-height: 100%; position: absolute; left: 0; width: 18px; text-align: center; } .pmo-map { margin: 20px -21px -18px; display: block; } .pmo-map img { width: 100%; } @media (max-width: 767px) { .c-timeline { background: #edecec; box-shadow: none; } .c-timeline .tab-nav { background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } .timeline { position: relative; } @media (min-width: 768px) { .timeline { padding: 50px; padding-left: 100px; } } @media (max-width: 767px) { .timeline { margin-top: 30px; } } .t-view { border: 1px solid #eee; position: relative; margin-bottom: 35px; } @media (max-width: 767px) { .t-view { background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } } .t-view .tv-header { padding: 16px 18px; border-bottom: 1px solid #eee; background: #F9F9F9; } .t-view .tv-header .actions { position: absolute; top: 5px; right: 10px; } .t-view .tv-body { padding: 23px 25px; } .t-view .tv-body .tvb-lightbox { margin: 0 -8px 15px; } .t-view .tv-body .tvb-lightbox [data-src] { padding: 0 5px; margin-bottom: 5px; } .t-view .tvh-user { display: block; } .t-view .tvh-user img { width: 46px; height: 46px; border-radius: 50%; } .t-view:before { position: absolute; width: 40px; height: 40px; border-radius: 50%; left: -70px; top: 0; border: 3px solid #FFF; text-align: center; font-size: 16px; line-height: 34px; color: #FFF; font-family: 'Material-Design-Iconic-Font'; z-index: 1; } .t-view:after { content: ""; position: absolute; top: 0; left: -50px; width: 1px; height: calc(100% + 37px); } .t-view[data-tv-type="text"]:before { content: "\f24f"; background: #00bcd4; box-shadow: 0 0 0 1px #00bcd4; } .t-view[data-tv-type="text"]:after { background: #00bcd4; } .t-view[data-tv-type="image"]:before { content: "\f17f"; background: #4caf50; box-shadow: 0 0 0 1px #4caf50; } .t-view[data-tv-type="image"]:after { background: #4caf50; } .t-view[data-tv-type="video"]:before { content: "\f3a9"; background: #ffc107; box-shadow: 0 0 0 1px #ffc107; } .t-view[data-tv-type="video"]:after { background: #ffc107; } .t-view .tvb-stats { list-style: none; padding: 0; margin: 10px 0 20px; } .t-view .tvb-stats > li { display: inline-block; padding: 5px 10px 6px; border: 1px solid #ccc; margin-right: 2px; } .t-view .tvb-stats > li i { font-size: 15px; line-height: 100%; vertical-align: top; margin-top: 2px; } .t-view .tvb-stats > li.tvbs-comments { border-color: #4caf50; color: #4caf50; } .t-view .tvb-stats > li.tvbs-likes { border-color: #03a9f4; color: #03a9f4; } .t-view .tvb-stats > li.tvbs-views { border-color: #ff9800; color: #ff9800; } .tv-comments .tvc-lists { padding: 0; list-style: none; margin: 0; } .tv-comments .tvc-lists > li { padding: 15px 20px; margin: 0; border-top: 1px solid #eee; } .tvc-more { color: #333; display: block; margin-bottom: -10px; } .tvc-more:hover { color: #000; } .tvc-more i { vertical-align: middle; margin-right: 5px; } .p-header { position: relative; margin: 0 -7px; } .p-header .actions { position: absolute; top: -18px; right: 0; } .p-menu { list-style: none; padding: 0 5px; margin: 0 0 30px; } .p-menu > li { display: inline-block; vertical-align: top; } .p-menu > li > a { display: block; padding: 5px 20px 5px 0; font-weight: 500; text-transform: uppercase; font-size: 15px; } .p-menu > li > a > i { margin-right: 4px; font-size: 20px; vertical-align: middle; margin-top: -5px; } .p-menu > li:not(.active) > a { color: #4285F4; } .p-menu > li:not(.active) > a:hover { color: #333; } .p-menu > li.active > a { color: #000; } @media (max-width: 991px) { .p-menu .pm-search { margin: 20px 2px 30px; display: block; } .p-menu .pm-search input[type="text"] { width: 100%; border: 1px solid #ccc; } } .p-menu .pms-inner { margin: -2px 0 0; position: relative; top: -2px; overflow: hidden; white-space: nowrap; } ================================================ FILE: material-manage/src/main/webapp/static/css/app.min.2.css ================================================ .p-menu .pms-inner i { vertical-align: top; font-size: 20px; line-height: 100%; position: absolute; left: 9px; top: 8px; color: #333; } .p-menu .pms-inner input[type="text"] { height: 35px; border-radius: 2px; padding: 0 10px 0 40px; } @media (min-width: 768px) { .p-menu .pms-inner input[type="text"] { border: 1px solid #fff; width: 50px; background: transparent; position: relative; z-index: 1; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .p-menu .pms-inner input[type="text"]:focus { border-color: #DFDFDF; width: 200px; } } .photos { margin: 2px 0 0; } .photos .lightbox { margin: 0 -8px; } .photos:not(.p-timeline) [data-src] { padding: 3px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 150ms; transition-duration: 150ms; } .p-timeline { position: relative; padding-left: 80px; margin-bottom: 75px; } .p-timeline [data-src] { float: left; width: 70px; height: 70px; margin: 0 3px 3px 0; } .p-timeline:last-child .pt-line:before { height: 100%; } .ptb-title { font-size: 15px; font-weight: 400; margin-bottom: 20px; } .pt-line { position: absolute; left: 0; top: 0; height: 100%; line-height: 14px; } .pt-line:before, .pt-line:after { content: ""; position: absolute; } .pt-line:before { width: 1px; height: calc(100% + 63px); background: #E2E2E2; top: 14px; right: -20px; } .pt-line:after { top: 2px; right: -26px; width: 13px; height: 13px; border: 1px solid #C1C1C1; border-radius: 50%; } .contacts:not(.c-profile) { padding: 0 8px; } .contacts > [class*="col-"] { padding: 0 10px; } .contacts .c-item { border: 1px solid #e2e2e2; border-radius: 2px; margin-bottom: 24px; } .contacts .c-item .ci-avatar { display: block; } .contacts .c-item .ci-avatar img { width: 100%; border-radius: 2px 2px 0 0; } .contacts .ci-avatar { margin: -1px -1px 0; } .contacts .c-info { text-align: center; margin-top: 15px; padding: 0 5px; } .contacts .c-info strong { color: #000; font-size: 14px; font-weight: 500; } .contacts .c-info small { color: #999; margin-top: 3px; } .contacts .c-info strong, .contacts .c-info small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; } .contacts .c-footer { border-top: 1px solid #e2e2e2; margin-top: 18px; } .contacts .c-footer > button { padding: 4px 10px 3px; display: block; width: 100%; text-align: center; color: #333; font-weight: 500; border-radius: 2px; background: #fff; border: 0; } .contacts .c-footer > button > i { font-size: 16px; vertical-align: middle; margin-top: -3px; } .z-depth-1 { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .z-depth-1-top { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); } .z-depth-1-bottom { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16); } .z-depth-2 { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } .z-depth-2-top { box-shadow: 0 6px 20px rgba(0, 0, 0, 0.19); } .z-depth-2-bottom { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2); } .z-depth-3 { box-shadow: 0 12px 15px rgba(0, 0, 0, 0.24), 0 17px 50px rgba(0, 0, 0, 0.19); } .z-depth-3-top { box-shadow: 0 17px 50px rgba(0, 0, 0, 0.19); } .z-depth-3-bottom { box-shadow: 0 12px 15px rgba(0, 0, 0, 0.24); } .z-depth-4 { box-shadow: 0 16px 28px rgba(0, 0, 0, 0.22), 0 25px 55px rgba(0, 0, 0, 0.21); } .z-depth-4-top { box-shadow: 0 25px 55px rgba(0, 0, 0, 0.21); } .z-depth-4-bottom { box-shadow: 0 16px 28px rgba(0, 0, 0, 0.22); } .z-depth-5 { box-shadow: 0 27px 24px rgba(0, 0, 0, 0.2), 0 40px 77px rgba(0, 0, 0, 0.22); } .z-depth-5-top { box-shadow: 0 40px 77px rgba(0, 0, 0, 0.22); } .z-depth-5-bottom { box-shadow: 0 27px 24px rgba(0, 0, 0, 0.2); } .z-depth-animation .z-depth-1, .z-depth-animation .z-depth-2, .z-depth-animation .z-depth-3, .z-depth-animation .z-depth-4, .z-depth-animation .z-depth-5 { transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } /* * Block Header * Used for Heading outside the Cards. */ .block-header { margin-bottom: 0; position: relative; } @media screen and (min-width: 768px) { .block-header { padding: 15px; background-color:#ffffff; } } @media screen and (max-width: 991px) { .block-header { padding: 0 18px; } } .block-header > h2 { font-size: 15px; color: #777; margin: 0; font-weight: 600; text-transform: uppercase; } .block-header > h2 > small { display: block; text-transform: none; margin-top: 8px; margin-bottom: 20px; color: #9E9E9E; line-height: 140%; } .block-header .actions { position: absolute; right: 10px; top: -5px; z-index: 4; } /* * Header Actions */ .actions { list-style: none; padding: 0; z-index: 3; margin: 0; } .actions > li { display: inline-block; vertical-align: baseline; } .actions > li > a, .actions > a { width: 30px; height: 30px; display: inline-block; text-align: center; padding-top: 5px; } .actions > li > a > i, .actions > a > i { color: #adadad; font-size: 20px; } .actions > li > a:hover > i, .actions > a:hover > i { color: #000; } @media (min-width: 768px) { .actions > li > a, .actions > a { position: relative; } .actions > li > a:before, .actions > a:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; -webkit-transform: scale3d(0, 0, 0); -moz-transform: scale3d(0, 0, 0); -ms-transform: scale3d(0, 0, 0); -o-transform: scale3d(0, 0, 0); transform: scale3d(0, 0, 0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; background-color: rgba(0, 0, 0, 0.1); z-index: 0; border-radius: 50%; opacity: 0; filter: alpha(opacity=0); } .actions > li > a:hover:before, .actions > a:hover:before, .actions > li > a.open:before, .actions > a.open:before { -webkit-transform: scale3d(1, 1, 1); -moz-transform: scale3d(1, 1, 1); -ms-transform: scale3d(1, 1, 1); -o-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); opacity: 1; filter: alpha(opacity=100); } } .actions > li.open > a > i, .actions.open > a > i { color: #000; } .actions > li.open > a:before, .actions.open > a:before { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .actions.actions-alt > li > a > i { color: #fff; } .actions.actions-alt > li > a > i:hover { color: #fff; } .actions.actions-alt > li.open > a > i { color: #fff; } .actions.open { z-index: 3; } /* * Collapse Menu Icons */ .line-wrap { width: 18px; height: 12px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; margin: 12px 20px; } .line-wrap .line { width: 18px; height: 2px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .line-wrap .line.center { margin: 3px 0; } .open .line-wrap { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); } .open .line-wrap .line.top { width: 12px; transform: translateX(8px) translateY(1px) rotate(45deg); -webkit-transform: translateX(8px) translateY(1px) rotate(45deg); } .open .line-wrap .line.bottom { width: 12px; transform: translateX(8px) translateY(-1px) rotate(-45deg); -webkit-transform: translateX(8px) translateY(-1px) rotate(-45deg); } /* * Load More */ .load-more { text-align: center; margin-top: 30px; } .load-more a { padding: 5px 10px 3px; display: inline-block; background-color: #f44336; color: #FFF; border-radius: 2px; white-space: nowrap; } .load-more a i { font-size: 20px; vertical-align: middle; position: relative; margin-top: -2px; } .load-more a:hover { background-color: #ea1c0d; } /* * Page Loader */ html:not(.ismobile) .page-loader { background: #fff; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; } html:not(.ismobile) .page-loader .preloader { width: 50px; position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -55px; -webkit-animation-name: fadeIn; animation-name: fadeIn; -webkit-animation-duration: 3000ms; animation-duration: 3000ms; -webkit-animation-fill-mode: both; animation-fill-mode: both; } html:not(.ismobile) .page-loader .preloader p { white-space: nowrap; position: relative; left: -9px; top: 22px; color: #CCC; } html.ismobile .page-loader { display: none; } .ie-warning { position: fixed; top: 0; left: 0; z-index: 9999; background: #000000; width: 100%; height: 100%; text-align: center; color: #fff; font-family: "Courier New", Courier, monospace; padding: 50px 0; } .ie-warning p { font-size: 17px; } .ie-warning .iew-container { min-width: 1024px; width: 100%; height: 200px; background: #fff; margin: 50px 0; } .ie-warning .iew-download { list-style: none; padding: 30px 0; margin: 0 auto; width: 720px; } .ie-warning .iew-download > li { float: left; vertical-align: top; } .ie-warning .iew-download > li > a { display: block; color: #000; width: 140px; font-size: 15px; padding: 15px 0; } .ie-warning .iew-download > li > a > div { margin-top: 10px; } .ie-warning .iew-download > li > a:hover { background-color: #eee; } #footer { position: absolute; bottom: 0; text-align: center; width: 100%; height: 110px; color: #a2a2a2; padding-top: 35px; padding-bottom: 15px; } #footer .f-menu { display: block; width: 100%; padding-left: 0; list-style: none; margin-left: -5px; margin-top: 8px; } #footer .f-menu > li { display: inline-block; padding-left: 5px; padding-right: 5px; } #footer .f-menu > li > a { color: #a2a2a2; } #footer .f-menu > li > a:hover { color: #777; } @media (min-width: 1199px) { body.sw-toggled #footer { padding-left: 268px; } } .pt-inner { text-align: center; } .pt-inner .pti-header { padding: 45px 10px 70px; color: #fff; position: relative; margin-bottom: 15px; } .pt-inner .pti-header > h2 { margin: 0; line-height: 100%; color: #fff; font-weight: 100; font-size: 50px; } .pt-inner .pti-header > h2 small { color: #fff; letter-spacing: 0; vertical-align: top; font-size: 16px; font-weight: 100; } .pt-inner .pti-header .ptih-title { background-color: rgba(0, 0, 0, 0.1); padding: 8px 10px 9px; text-transform: uppercase; margin: 0 -10px; position: absolute; width: 100%; bottom: 0; } .pt-inner .pti-body { padding: 0 23px; } .pt-inner .pti-body .ptib-item { padding: 15px 0; font-weight: 400; } .pt-inner .pti-body .ptib-item:not(:last-child) { border-bottom: 1px solid #eee; } .pt-inner .pti-footer { padding: 10px 20px 30px; } .pt-inner .pti-footer > a { width: 60px; height: 60px; border-radius: 50%; text-align: center; color: #fff; display: inline-block; line-height: 60px; font-size: 30px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .pt-inner .pti-footer > a:hover { opacity: 0.85; filter: alpha(opacity=85); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } .invoice { min-width: 1100px; max-width: 1170px; } .i-logo { width: 150px; } .i-table .highlight { background-color: #eee; border-bottom: 1px solid #e6e6e6; } .i-table td.highlight { font-size: 14px; font-weight: 500; } .wall-attrs { margin-bottom: 0; } .wa-stats { float: left; } .wa-stats > span { margin-right: -1px; padding: 7px 12px; border: 1px solid #E0E0E0; float: left; font-weight: 500; } .wa-stats > span.active { color: #4caf50; } .wa-stats > span:first-child { border-radius: 2px 0 0 2px; } .wa-stats > span:last-child { border-radius: 0 2px 2px 0; } .wa-stats > span > i { line-height: 100%; vertical-align: top; position: relative; top: 2px; font-size: 15px; margin-right: 2px; } .wa-users { float: right; padding: 0 !important; margin-right: -5px; } .wa-users > a { display: inline-block; margin-left: 2px; } .wa-users > a > img { width: 33px; height: 33px; border-radius: 50%; } .wa-users > a > img:hover { opacity: 0.85; filter: alpha(opacity=85); } .wcc-inner { border: 1px solid #E4E4E4; padding: 10px 15px; resize: none; border-radius: 2px; background: #fff; color: #9A9A9A; cursor: pointer; } .wcci-text { border: 0; display: block; width: 100%; resize: none; padding: 0; } .wall-comment-list { padding: 20px; background: #f7f7f7; } .wall-comment-list .media { position: relative; } .wall-comment-list .media:hover .actions { display: block; } .wall-comment-list .actions { display: none; position: absolute; right: -20px; top: -1px; } .wcl-list + .wcl-form { margin-top: 25px; } .wp-text { border: 0; padding: 0; display: block; width: 100%; resize: none; } .wp-media { background: #f7f7f7; border: 1px solid #E4E4E4; padding: 12px 15px; margin-top: 25px; text-align: center; } .wpb-actions { background: #f7f7f7; margin: 0; padding: 10px 20px; } .wpb-actions > li:not(.pull-right) { float: left; } [data-wpba="image"] { color: #4caf50; } [data-wpba="image"]:hover { color: #449d48; } [data-wpba="video"] { color: #ff9800; } [data-wpba="video"]:hover { color: #e68900; } [data-wpba="link"] { color: #00bcd4; } [data-wpba="link"]:hover { color: #00a5bb; } .wpba-attrs > ul > li { padding: 0; margin-right: 5px; } .wpba-attrs > ul > li > a { display: block; width: 22px; } .wpba-attrs > ul > li > a > i { font-size: 20px; } .wpba-attrs > ul > li.active i { color: #333; } .wall-img-preview { text-align: center; } @media screen and (min-width: 768px) { .wall-img-preview { margin: 0 -23px 20px; } } @media screen and (max-width: 991px) { .wall-img-preview { margin: 0 -16px 20px; } } .wall-img-preview .wip-item { display: block; float: left; position: relative; overflow: hidden; border: 2px solid #fff; background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } .wall-img-preview .wip-item > img { display: none; } .wall-img-preview .wip-item:first-child:nth-last-child(2), .wall-img-preview .wip-item:first-child:nth-last-child(2) ~ div { width: 50%; padding-bottom: 40%; } .wall-img-preview .wip-item:first-child:nth-last-child(3), .wall-img-preview .wip-item:first-child:nth-last-child(3) ~ div, .wall-img-preview .wip-item:first-child:nth-last-child(4), .wall-img-preview .wip-item:first-child:nth-last-child(4) ~ div:not(:last-child), .wall-img-preview .wip-item:first-child:nth-last-child(5), .wall-img-preview .wip-item:first-child:nth-last-child(5) ~ div:not( :nth-last-of-type(-n+2)), .wall-img-preview .wip-item:first-child:nth-last-child(6), .wall-img-preview .wip-item:first-child:nth-last-child(6) ~ div, .wall-img-preview .wip-item:first-child:nth-last-child(7) ~ div:nth-last-of-type(-n+3) { width: 33.333333%; padding-bottom: 30%; } .wall-img-preview .wip-item:first-child:nth-last-child(5) ~ div:nth-last-of-type(-n+2) { width: 50%; padding-bottom: 40%; } .wall-img-preview .wip-item:first-child:nth-last-child(7), .wall-img-preview .wip-item:first-child:nth-last-child(7) ~ div:not( :nth-last-of-type(-n+3)), .wall-img-preview .wip-item:first-child:nth-last-child(n+8), .wall-img-preview .wip-item:first-child:nth-last-child(n+8) ~ div { width: 25%; padding-bottom: 22%; } .wall-img-preview .wip-item:only-child, .wall-img-preview .wip-item:first-child:nth-last-child(4) ~ div:nth-child(4) { width: 100%; padding-bottom: 50%; } /* * For header type 1 only * You may remove these if you opt header 2 */ #header .skin-switch { padding: 10px 0 2px; text-align: center; } #header .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; display: inline-block; margin: 2px 3px; } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For header type 2 only * You may remove these if you opt header 1 */ #header-2 .skin-switch { position: absolute; right: 50px; bottom: 23px; z-index: 1; } #header-2 .skin-switch .btn { background: #fff; width: 50px; height: 50px; border-radius: 50%; font-size: 25px; z-index: 2; } #header-2 .skin-switch .dropdown-menu { min-width: 130px; height: 130px; border-radius: 50%; width: 130px; top: -42px; left: -40px; z-index: 1; -webkit-transform-origin: center; -moz-transform-origin: center; -ms-transform-origin: center; transform-origin: center; -webkit-transform: scale(0) rotate(-360deg); -ms-transform: scale(0) rotate(-360deg); -o-transform: scale(0) rotate(-360deg); transform: scale(0) rotate(-360deg); -webkit-transition-duration: 500ms; transition-duration: 500ms; } #header-2 .skin-switch .dropdown-menu .ss-skin { position: absolute; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-1 { margin-left: -8px; top: 12px; left: 50%; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-2 { right: 24px; top: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-3 { top: 50%; margin-top: -8px; right: 12px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-4 { right: 24px; bottom: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-5 { margin-left: -8px; bottom: 12px; left: 50%; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-6 { left: 24px; bottom: 26px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-7 { top: 50%; margin-top: -8px; left: 12px; } #header-2 .skin-switch .dropdown-menu .ss-skin.ss-8 { left: 24px; top: 26px; } #header-2 .skin-switch.open .dropdown-menu { -webkit-transform: scale(1) rotate(0deg); -ms-transform: scale(1) rotate(0deg); -o-transform: scale(1) rotate(0deg); transform: scale(1) rotate(0deg); } /* ----------------------------- End header type 2 ------------------------------------- */ /* * Do not remove these * This is common for both */ .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; } .ss-skin:hover { opacity: 0.8; filter: alpha(opacity=80); } [data-current-skin="lightblue"] { background-color: #03a9f4; } [data-current-skin="lightblue"] .ss-icon { color: #03a9f4; } @media (max-width: 767px) { [data-current-skin="lightblue"] .ha-menu { background: #03a9f4; } } [data-current-skin="bluegray"] { background-color: #607d8b; } [data-current-skin="bluegray"] .ss-icon { color: #607d8b; } @media (max-width: 767px) { [data-current-skin="bluegray"] .ha-menu { background: #607d8b; } } [data-current-skin="blue"] { background-color: #2196f3; } [data-current-skin="blue"] .ss-icon { color: #2196f3; } @media (max-width: 767px) { [data-current-skin="blue"] .ha-menu { background: #2196f3; } } [data-current-skin="purple"] { background-color: #9c27b0; } [data-current-skin="purple"] .ss-icon { color: #9c27b0; } @media (max-width: 767px) { [data-current-skin="purple"] .ha-menu { background: #9c27b0; } } [data-current-skin="orange"] { background-color: #ff9800; } [data-current-skin="orange"] .ss-icon { color: #ff9800; } @media (max-width: 767px) { [data-current-skin="orange"] .ha-menu { background: #ff9800; } } [data-current-skin="cyan"] { background-color: #00bcd4; } [data-current-skin="cyan"] .ss-icon { color: #00bcd4; } @media (max-width: 767px) { [data-current-skin="cyan"] .ha-menu { background: #00bcd4; } } [data-current-skin="green"] { background-color: #4caf50; } [data-current-skin="green"] .ss-icon { color: #4caf50; } @media (max-width: 767px) { [data-current-skin="green"] .ha-menu { background: #4caf50; } } [data-current-skin="teal"] { background-color: #009688; } [data-current-skin="teal"] .ss-icon { color: #009688; } @media (max-width: 767px) { [data-current-skin="teal"] .ha-menu { background: #009688; } } [data-current-skin="pink"] { background-color: #e91e63; } [data-current-skin="pink"] .ss-icon { color: #e91e63; } @media (max-width: 767px) { [data-current-skin="pink"] .ha-menu { background: #e91e63; } } .preloader { position: relative; margin: 0px auto; display: inline-block; } .preloader:not([class*="pl-"]) { width: 40px; } .preloader:before { content: ''; display: block; padding-top: 100%; } .preloader.pl-xs { width: 20px; } .preloader.pl-sm { width: 30px; } .preloader.pl-lg { width: 50px; } .preloader.pl-xl { width: 80px; } .preloader.pl-xxl { width: 100px; } .preloader:not([class*="pls-"]) .plc-path { animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite; } .preloader[class*="pls-"] .plc-path { animation: dash 1.5s ease-in-out infinite; } .preloader.pls-red .plc-path { stroke: #f44336; } .preloader.pls-blue .plc-path { stroke: #2196f3; } .preloader.pls-green .plc-path { stroke: #4caf50; } .preloader.pls-yellow .plc-path { stroke: #ffeb3b; } .preloader.pls-bluegray .plc-path { stroke: #607d8b; } .preloader.pls-amber .plc-path { stroke: #ffc107; } .preloader.pls-teal .plc-path { stroke: #009688; } .preloader.pls-gray .plc-path { stroke: #9e9e9e; } .preloader.pls-pink .plc-path { stroke: #e91e63; } .preloader.pls-purple .plc-path { stroke: #9c27b0; } .preloader.pls-white .plc-path { stroke: #fff; } .pl-circular { animation: rotate 2s linear infinite; height: 100%; transform-origin: center center; width: 100%; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; } .plc-path { stroke-dasharray: 1,200; stroke-dashoffset: 0; stroke-linecap: round; stroke-width: 2; stroke-miterlimit: 10; fill: none; } @keyframes rotate { 100% { transform: rotate(360deg); } } @keyframes dash { 0% { stroke-dasharray: 1,200; stroke-dashoffset: 0; } 50% { stroke-dasharray: 89,200; stroke-dashoffset: -35px; } 100% { stroke-dasharray: 89,200; stroke-dashoffset: -124px; } } @keyframes color { 100%, 0% { stroke: #f44336; } 40% { stroke: #2196f3; } 66% { stroke: #4caf50; } 80%, 90% { stroke: #ffc107; } } @media print { @page { margin: 0; size: auto; } body { margin: 0mm 0mm 0mm 0mm !important; padding: 0mm !important; } #header, #footer, #sidebar, #chat, .growl-animated, .m-btn { display: none !important; } /* * INVOICE */ .invoice { padding: 30px !important; -webkit-print-color-adjust: exact !important; } .invoice .card-header { background: #eee !important; padding: 20px; margin-bottom: 20px; margin: -60px -30px 25px -30px; } .invoice .block-header { display: none; } .invoice .highlight { background: #eee !important; } } /* * Vendor Overrides */ .mejs-container { outline: none; } .mejs-container .mejs-controls { background: #ec592f; height: 50px; padding: 10px 5px 0; } .mejs-container .mejs-controls div { height: 5px; } .mejs-container .mejs-controls div.mejs-time-rail { position: absolute; left: 0; top: 0; padding: 0; width: 100% !important; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-total { margin: 0; width: 100% !important; background: #ec592f; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-loaded { background: #D04B25; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-current { background: #ffea00; } .mejs-container .mejs-controls div.mejs-time-rail .mejs-time-buffering { background: #ec592f; } .mejs-container .mejs-controls div.mejs-time-rail span:not(.mejs-time-float), .mejs-container .mejs-controls div.mejs-time-rail a { border-radius: 0; height: 3px; } .mejs-container .mejs-controls .mejs-button button { background-color: #ec592f; width: 15px; height: 15px; background-position: center; } .mejs-container .mejs-controls .mejs-button button:focus { outline: none !important; } .mejs-container .mejs-controls .mejs-volume-button { position: absolute; right: 35px; } .mejs-container .mejs-controls .mejs-play button { background-image: url("../img/icons/play.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-play button { background-image: url("../img/icons/play@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-pause button { background-image: url("../img/icons/pause.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-pause button { background-image: url("../img/icons/pause@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-mute button { background-image: url("../img/icons/speaker.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-mute button { background-image: url("../img/icons/speaker@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-unmute button { background-image: url("../img/icons/speaker-2.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-unmute button { background-image: url("../img/icons/speaker-2@2x.png"); background-size: 15px 15px; } } .mejs-container .mejs-controls .mejs-fullscreen-button { position: absolute; right: 5px; } .mejs-container .mejs-controls .mejs-fullscreen-button button { background-image: url("../img/icons/fullscreen.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .mejs-container .mejs-controls .mejs-fullscreen-button button { background-image: url("../img/icons/fullscreen@2x.png"); background-size: 15px 15px; } } /** CALENDAR WIDGET **/ #calendar-widget { margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } #fc-actions { position: absolute; bottom: 10px; right: 12px; } .fc { background-color: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); margin-bottom: 30px; } .fc td, .fc th { border-color: #f0f0f0; } .fc th { font-weight: 400; } .fc table { background: transparent; } .fc table tr > td:first-child { border-left-width: 0; } #calendar-widget .fc-toolbar { background: #009688; } #calendar-widget .fc-day-header { color: #fff; background: #007d71; padding: 5px 0; border-width: 0; } #calendar-widget .fc-day-number { text-align: center; color: #ADADAD; padding: 5px 0; } #calendar-widget .fc-day-grid-event { margin: 1px 3px 1px; } #calendar-widget .ui-widget-header th, #calendar-widget .ui-widget-header { border-width: 0; } #calendar .fc-toolbar { height: 300px; background-image: url('../img/cal-header.jpg'); background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; background-position: inherit; } #calendar .fc-toolbar:before { content: ""; height: 50px; width: 100%; background: rgba(0, 0, 0, 0.36); position: absolute; bottom: 0; left: 0; } #calendar .fc-toolbar .fc-center { margin-top: 238px; position: relative; } @media screen and (max-width: 991px) { #calendar .fc-toolbar { height: 200px; } #calendar .fc-toolbar .fc-center { margin-top: 138px; } } #calendar .fc-day-header { color: #ADADAD; text-align: left; font-size: 14px; border-bottom-width: 0; border-right-color: #eee; padding: 10px 12px; } #calendar .fc-day-number { padding-left: 10px !important; color: #CCC; text-align: left !important; } @media screen and (min-width: 991px) { #calendar .fc-day-number { font-size: 25px; letter-spacing: -2px; } } #calendar .fc-day-grid-event { margin: 1px 9px 0; } .fc-today { color: #ffc107; } .fc-toolbar { margin-bottom: 0; padding: 20px 7px 19px; position: relative; } .fc-toolbar h2 { margin-top: 7px; font-size: 20px; font-weight: 400; text-transform: uppercase; color: #fff; } .fc-toolbar .ui-button { border: 0; background: 0 0; padding: 0; outline: none !important; text-align: center; width: 30px; height: 30px; border-radius: 50%; margin-top: 2px; color: #fff; } .fc-toolbar .ui-button:hover { background: #fff; color: #009688; } .fc-toolbar .ui-button > span { position: relative; font-family: 'Material-Design-Iconic-Font'; font-size: 20px; line-height: 100%; width: 30px; display: block; margin-top: 2px; } .fc-toolbar .ui-button > span:before { position: relative; z-index: 1; } .fc-toolbar .ui-button > span.ui-icon-circle-triangle-w:before { content: "\f2fa"; } .fc-toolbar .ui-button > span.ui-icon-circle-triangle-e:before { content: "\f2fb"; } .fc-event { padding: 0; font-size: 11px; border-radius: 0; border: 0; } .fc-event .fc-title { padding: 2px 8px; display: block; } .fc-event .fc-time { float: left; background: rgba(0, 0, 0, 0.2); padding: 2px 6px; margin: 0 0 0 -1px; } .fc-view, .fc-view > table { border: 0; overflow: hidden; } .fc-view > table > tbody > tr > .ui-widget-content { border-top: 0; } div.fc-row { margin-right: 0 !important; border: 0 !important; } .fc-today { color: #ffc107 !important; } /* Even Tag Color */ .event-tag { margin-top: 5px; } .event-tag > span { border-radius: 50%; width: 30px; height: 30px; margin-right: 3px; position: relative; display: inline-block; cursor: pointer; } .event-tag > span:hover { opacity: 0.8; filter: alpha(opacity=80); } .event-tag > span.selected:before { font-family: 'Material-Design-Iconic-Font'; content: "\f26b"; position: absolute; text-align: center; top: 3px; width: 100%; font-size: 17px; color: #FFF; } hr.fc-divider { border-width: 1px; border-color: #eee; } .fc-day-grid-container.fc-scroller { height: auto !important; overflow: hidden !important; } .bootgrid-footer .infoBar, .bootgrid-header .actionBar { text-align: left; } .bootgrid-footer .search, .bootgrid-header .search { vertical-align: top; } .bootgrid-header { padding: 0 25px 10px; } .bootgrid-header .search { border: 1px solid #e0e0e0; } .bootgrid-header .search .form-control, .bootgrid-header .search .input-group-addon { border: 0; } .bootgrid-header .search .glyphicon-search { vertical-align: top; padding: 9px 10px 0; } .bootgrid-header .search .glyphicon-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; font-size: 17px; vertical-align: top; line-height: 100%; } @media (min-width: 480px) { .bootgrid-header .search { width: 300px; } } @media (max-width: 480px) { .bootgrid-header .search { width: 100%; padding-right: 90px; } } .bootgrid-header .actions { box-shadow: none; } .bootgrid-header .actions .btn-group { border: 1px solid #e0e0e0; } .bootgrid-header .actions .btn-group .btn { height: 35px; box-shadow: none !important; background: transparent; } .bootgrid-header .actions .btn-group .dropdown-menu { padding: 10px 20px; } .bootgrid-header .actions .btn-group .dropdown-menu .dropdown-item { padding: 0 0 0 27px !important; } .bootgrid-header .actions .btn-group .dropdown-menu .dropdown-item:hover { background-color: #fff !important; } @media (min-width: 768px) { .bootgrid-header .actions .btn-group .dropdown-menu { left: 0; -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; margin-top: 1px; } } .bootgrid-header .actions .btn-group .caret { display: none; } .bootgrid-header .actions .btn-group .zmdi { line-height: 100%; font-size: 18px; vertical-align: top; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .bootgrid-header .actions .btn-group.open .zmdi { -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg); } @media (max-width: 480px) { .bootgrid-header .actions { position: absolute; top: 0; right: 15px; } } .bootgrid-table th > .column-header-anchor > .icon { top: 0px; font-size: 20px; line-height: 100%; } .bootgrid-footer .col-sm-6 { padding: 10px 30px 20px; } @media (max-width: 768px) { .bootgrid-footer .col-sm-6 { text-align: center; } } @media (max-width: 768px) { .bootgrid-footer .infoBar { display: none; } } .bootgrid-footer .infoBar .infos { border: 1px solid #EEE; display: inline-block; float: right; padding: 7px 30px; font-size: 12px; margin-top: 5px; } .select-cell .checkbox { margin: 0; } .command-edit, .command-delete { background: #fff; } .bootstrap-select .dropdown-menu { padding: 0; } .bootstrap-select .dropdown-toggle:focus { outline: none !important; } .bootstrap-select > .btn-default { background: none !important; border-bottom: 1px solid #e0e0e0 !important; border-radius: 0; padding-left: 0; padding-right: 0; } .bootstrap-select > .btn-default:before { position: absolute; top: 0; right: 0; content: ""; height: calc(100% - 2px); width: 30px; background-color: #FFF; background-position: right calc(100% - 7px); background-repeat: no-repeat; background-image: url("../img/select.png"); pointer-events: none; z-index: 5; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .bootstrap-select > .btn-default:before { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } .bootstrap-select > .btn-default:after { position: absolute; z-index: 3; bottom: -1px; left: 0; height: 2px; width: 0; content: ""; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .bootstrap-select > .btn-default:not(.disabled):after, .bootstrap-select > .btn-default:not(.readonly):after { background: #2196f3; } .bootstrap-select > .btn-default.disabled:after, .bootstrap-select > .btn-default.readonly:after { background: #ccc; } .bootstrap-select.open > .btn-default:after { width: 100%; } .bootstrap-select .bs-searchbox { padding: 5px 5px 5px 40px; position: relative; background: #f7f7f7; } .bootstrap-select .bs-searchbox:before { position: absolute; left: 0; top: 0; width: 40px; height: 100%; content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; font-size: 25px; padding: 4px 0 0 15px; } .bootstrap-select .bs-searchbox input { border: 0; background: transparent; } .bootstrap-select.btn-group .dropdown-menu li a.opt { padding-left: 17px; } .bootstrap-select .check-mark { margin-top: -5px !important; font-size: 19px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); display: block !important; position: absolute; top: 11px; right: 15px; } .bootstrap-select .check-mark:before { content: "\f26b"; font-family: 'Material-Design-Iconic-Font'; } .bootstrap-select .selected .check-mark { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); } .bootstrap-select .notify { bottom: 0 !important; margin: 0 !important; width: 100% !important; border: 0 !important; background: #f44336 !important; color: #fff !important; text-align: center; } .bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) { width: 100%; } .chosen-container .chosen-drop { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); margin-top: 1px; border: 0; left: 0; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; -webkit-transition: transform opacity; -o-transition: transform opacity; transition: transform opacity; -webkit-transition-duration: 250ms; transition-duration: 250ms; } .chosen-container.chosen-with-drop .chosen-drop { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .chosen-container.chosen-with-drop .chosen-single:after { width: 100%; } .chosen-container .chosen-results { margin: 0; padding: 0; max-height: 300px; } .chosen-container .chosen-results li { padding: 10px 17px; width: 100%; } .chosen-container .chosen-results li.highlighted { background: rgba(0, 0, 0, 0.075); color: #333333; } .chosen-container .chosen-results li.result-selected { background: transparent; color: #5e5e5e; position: relative; } .chosen-container .chosen-results li.result-selected:before { content: "\f26b"; font-family: 'Material-Design-Iconic-Font'; position: absolute; right: 15px; top: 10px; font-size: 19px; } .chosen-container .chosen-results li.group-result { color: #B2B2B2; font-weight: normal; padding: 16px 15px 6px; margin-top: 9px; } .chosen-container .chosen-results li.group-result:not(:first-child) { border-top: 1px solid #eee; } .chosen-container-single .chosen-single { border-radius: 0; overflow: visible; height: 34px; padding: 6px 0 6px; text-transform: uppercase; border: 0; border-bottom: 1px solid #e0e0e0; background: none; box-shadow: none; } .chosen-container-single .chosen-single:after { content: ""; width: 0; background: #2196f3; height: 2px; position: absolute; left: 0; bottom: -1px; -webkit-transition: width; -o-transition: width; transition: width; -webkit-transition-duration: 300ms; transition-duration: 300ms; } .chosen-container-single .chosen-single div b { background-image: url("../img/select.png"); background-repeat: no-repeat; background-position: right 12px; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .chosen-container-single .chosen-single div b { background-image: url("../img/select@2x.png"); background-size: 12px 12px; } } .chosen-container-single .chosen-search { padding: 5px 5px 5px 40px; background: #f7f7f7; } .chosen-container-single .chosen-search:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 0; top: 0; width: 40px; height: 100%; font-size: 25px; padding: 5px 0 0 15px; } .chosen-container-single .chosen-search input[type=text] { border: 0; height: 35px; line-height: 1.42857143; background: none; } .chosen-container-active.chosen-with-drop .chosen-single { border: 0; background: none; } .chosen-container-multi .chosen-choices { padding: 0; border: 0; border-bottom: 1px solid #e0e0e0; background: none; box-shadow: none; } .chosen-container-multi .chosen-choices li.search-choice { border-radius: 2px; margin: 4px 4px 0 0; background: #eaeaea; padding: 5px 23px 5px 8px; border: 0; box-shadow: none; } .chosen-container-multi .chosen-choices li.search-choice .search-choice-close { background-image: none; } .chosen-container-multi .chosen-choices li.search-choice .search-choice-close:before { display: inline-block; font-family: 'Material-Design-Iconic-Font'; content: "\f135"; position: relative; top: 1px; color: #9C9C9C; z-index: 2; font-size: 12px; } .chosen-container-multi .chosen-choices li.search-field input[type=text] { padding: 0; height: 31px; } select.chosen { display: none; } .noUi-target { border-radius: 0; box-shadow: none; border: 0; } .noUi-background { background: #d4d4d4; box-shadow: none; } .noUi-horizontal { height: 3px; } .noUi-horizontal .noUi-handle { top: -8px; } .noUi-vertical { width: 3px; } .noUi-horizontal .noUi-handle, .noUi-vertical .noUi-handle { width: 19px; height: 19px; border: 0; border-radius: 100%; box-shadow: none; -webkit-transition: box-shadow; -o-transition: box-shadow; transition: box-shadow; -webkit-transition-duration: 200ms; transition-duration: 200ms; cursor: pointer; position: relative; } .noUi-horizontal .noUi-handle:before, .noUi-vertical .noUi-handle:before, .noUi-horizontal .noUi-handle:after, .noUi-vertical .noUi-handle:after { display: none; } .noUi-horizontal .noUi-handle:active, .noUi-vertical .noUi-handle:active { background: #ccc !important; } .noUi-horizontal .noUi-handle .is-tooltip, .noUi-vertical .noUi-handle .is-tooltip { position: absolute; bottom: 32px; height: 35px; border-radius: 2px; color: #fff; text-align: center; line-height: 33px; width: 50px; left: 50%; margin-left: -25px; padding: 0 10px; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; opacity: 0; filter: alpha(opacity=0); -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); } .noUi-horizontal .noUi-handle .is-tooltip:after, .noUi-vertical .noUi-handle .is-tooltip:after { width: 0; height: 0; border-style: solid; border-width: 15px 10px 0 10px; position: absolute; bottom: -8px; left: 50%; margin-left: -9px; content: ""; } .noUi-horizontal .noUi-active, .noUi-vertical .noUi-active { box-shadow: 0 0 0 13px rgba(0, 0, 0, 0.1); } .noUi-horizontal .noUi-active .is-tooltip, .noUi-vertical .noUi-active .is-tooltip { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); bottom: 40px; opacity: 1; filter: alpha(opacity=100); } .input-slider:not([data-is-color]) .noUi-handle, .input-slider-range:not([data-is-color]) .noUi-handle, .input-slider-values:not([data-is-color]) .noUi-handle, .input-slider:not([data-is-color]) .noUi-connect, .input-slider-range:not([data-is-color]) .noUi-connect, .input-slider-values:not([data-is-color]) .noUi-connect { background: #009688 !important; } .input-slider:not([data-is-color]) .is-tooltip, .input-slider-range:not([data-is-color]) .is-tooltip, .input-slider-values:not([data-is-color]) .is-tooltip { background: #009688; } .input-slider:not([data-is-color]) .is-tooltip:after, .input-slider-range:not([data-is-color]) .is-tooltip:after, .input-slider-values:not([data-is-color]) .is-tooltip:after { border-color: #009688 transparent transparent transparent; } .input-slider[data-is-color=red] .noUi-handle, .input-slider-range[data-is-color=red] .noUi-handle, .input-slider-values[data-is-color=red] .noUi-handle, .input-slider[data-is-color=red] .noUi-connect, .input-slider-range[data-is-color=red] .noUi-connect, .input-slider-values[data-is-color=red] .noUi-connect { background: #f44336 !important; } .input-slider[data-is-color=blue] .noUi-handle, .input-slider-range[data-is-color=blue] .noUi-handle, .input-slider-values[data-is-color=blue] .noUi-handle, .input-slider[data-is-color=blue] .noUi-connect, .input-slider-range[data-is-color=blue] .noUi-connect, .input-slider-values[data-is-color=blue] .noUi-connect { background: #2196f3 !important; } .input-slider[data-is-color=cyan] .noUi-handle, .input-slider-range[data-is-color=cyan] .noUi-handle, .input-slider-values[data-is-color=cyan] .noUi-handle, .input-slider[data-is-color=cyan] .noUi-connect, .input-slider-range[data-is-color=cyan] .noUi-connect, .input-slider-values[data-is-color=cyan] .noUi-connect { background: #00bcd4 !important; } .input-slider[data-is-color=amber] .noUi-handle, .input-slider-range[data-is-color=amber] .noUi-handle, .input-slider-values[data-is-color=amber] .noUi-handle, .input-slider[data-is-color=amber] .noUi-connect, .input-slider-range[data-is-color=amber] .noUi-connect, .input-slider-values[data-is-color=amber] .noUi-connect { background: #ffc107 !important; } .input-slider[data-is-color=green] .noUi-handle, .input-slider-range[data-is-color=green] .noUi-handle, .input-slider-values[data-is-color=green] .noUi-handle, .input-slider[data-is-color=green] .noUi-connect, .input-slider-range[data-is-color=green] .noUi-connect, .input-slider-values[data-is-color=green] .noUi-connect { background: #4caf50 !important; } .input-slider .noUi-origin { background: #d4d4d4; } .input-slider:not([data-is-color]) .noUi-base { background: #009688 !important; } .input-slider[data-is-color=red] .noUi-base { background: #f44336 !important; } .input-slider[data-is-color=blue] .noUi-base { background: #2196f3 !important; } .input-slider[data-is-color=cyan] .noUi-base { background: #00bcd4 !important; } .input-slider[data-is-color=amber] .noUi-base { background: #ffc107 !important; } .input-slider[data-is-color=green] .noUi-base { background: #4caf50 !important; } .cp-container { position: relative; } .cp-container > .input-group input.cp-value { color: #000 !important; background: transparent !important; } .cp-container > .input-group .dropdown-menu { padding: 20px; margin-left: 10px; } .cp-container i.cp-value { width: 25px; height: 25px; border-radius: 2px; position: absolute; top: 0; right: 15px; } .note-editor .note-toolbar, .note-popover .note-toolbar, .note-editor .popover-content, .note-popover .popover-content { background: #fff; border-color: #e4e4e4; margin: 0; padding: 10px 0 15px; text-align: center; } .note-editor .note-toolbar > .btn-group, .note-popover .note-toolbar > .btn-group, .note-editor .popover-content > .btn-group, .note-popover .popover-content > .btn-group { display: inline-block; float: none; box-shadow: none; } .note-editor .note-toolbar > .btn-group .btn, .note-popover .note-toolbar > .btn-group .btn, .note-editor .popover-content > .btn-group .btn, .note-popover .popover-content > .btn-group .btn { margin: 0 1px; } .note-editor .note-toolbar > .btn-group > .active, .note-popover .note-toolbar > .btn-group > .active, .note-editor .popover-content > .btn-group > .active, .note-popover .popover-content > .btn-group > .active { background: #00bcd4; color: #fff; } .note-editor .note-toolbar .btn, .note-popover .note-toolbar .btn, .note-editor .popover-content .btn, .note-popover .popover-content .btn { height: 40px; border-radius: 2px !important; box-shadow: none !important; } .note-editor .note-toolbar .btn:active, .note-popover .note-toolbar .btn:active, .note-editor .popover-content .btn:active, .note-popover .popover-content .btn:active { box-shadow: none; } .note-editor .note-toolbar .note-palette-title, .note-popover .note-toolbar .note-palette-title, .note-editor .popover-content .note-palette-title, .note-popover .popover-content .note-palette-title { margin: 0 !important; padding: 10px 0 !important; font-size: 13px !important; text-align: center !important; border: 0 !important; } .note-editor .note-toolbar .note-color-reset, .note-popover .note-toolbar .note-color-reset, .note-editor .popover-content .note-color-reset, .note-popover .popover-content .note-color-reset { padding: 0 0 10px !important; margin: 0 !important; background: none; text-align: center; } .note-editor .note-toolbar .note-color .dropdown-menu, .note-popover .note-toolbar .note-color .dropdown-menu, .note-editor .popover-content .note-color .dropdown-menu, .note-popover .popover-content .note-color .dropdown-menu { min-width: 335px; } .note-editor .note-statusbar .note-resizebar, .note-popover .note-statusbar .note-resizebar { border-color: #E8E8E8; } .note-editor .note-statusbar .note-resizebar .note-icon-bar, .note-popover .note-statusbar .note-resizebar .note-icon-bar { border-color: #BCBCBC; } .note-editor .fa, .note-popover .fa { font-style: normal; font-size: 20px; vertical-align: middle; } .note-editor .fa:before, .note-popover .fa:before { font-family: 'Material-Design-Iconic-Font'; } .note-editor .fa.fa-magic:before, .note-popover .fa.fa-magic:before { content: "\f16a"; } .note-editor .fa.fa-bold:before, .note-popover .fa.fa-bold:before { content: "\f23d"; } .note-editor .fa.fa-italic:before, .note-popover .fa.fa-italic:before { content: "\f245"; } .note-editor .fa.fa-underline:before, .note-popover .fa.fa-underline:before { content: "\f24f"; } .note-editor .fa.fa-font:before, .note-popover .fa.fa-font:before { content: "\f242"; } .note-editor .fa.fa-list-ul:before, .note-popover .fa.fa-list-ul:before { content: "\f247"; } .note-editor .fa.fa-list-ol:before, .note-popover .fa.fa-list-ol:before { content: "\f248"; } .note-editor .fa.fa-align-left:before, .note-popover .fa.fa-align-left:before { content: "\f23b"; } .note-editor .fa.fa-align-right:before, .note-popover .fa.fa-align-right:before { content: "\f23c"; } .note-editor .fa.fa-align-center:before, .note-popover .fa.fa-align-center:before { content: "\f239"; } .note-editor .fa.fa-align-justify:before, .note-popover .fa.fa-align-justify:before { content: "\f23a"; } .note-editor .fa.fa-indent:before, .note-popover .fa.fa-indent:before { content: "\f244"; } .note-editor .fa.fa-outdent:before, .note-popover .fa.fa-outdent:before { content: "\f243"; } .note-editor .fa.fa-text-height:before, .note-popover .fa.fa-text-height:before { content: "\f246"; } .note-editor .fa.fa-table:before, .note-popover .fa.fa-table:before { content: "\f320"; } .note-editor .fa.fa-link:before, .note-popover .fa.fa-link:before { content: "\f18e"; } .note-editor .fa.fa-picture-o:before, .note-popover .fa.fa-picture-o:before { content: "\f17f"; } .note-editor .fa.fa-minus:before, .note-popover .fa.fa-minus:before { content: "\f22f"; } .note-editor .fa.fa-arrows-alt:before, .note-popover .fa.fa-arrows-alt:before { content: "\f16d"; } .note-editor .fa.fa-code:before, .note-popover .fa.fa-code:before { content: "\f13a"; } .note-editor .fa.fa-question:before, .note-popover .fa.fa-question:before { content: "\f1f5"; } .note-editor .fa.fa-eraser:before, .note-popover .fa.fa-eraser:before { content: "\f23f"; } .note-editor .fa.fa-square:before, .note-popover .fa.fa-square:before { content: "\f279"; } .note-editor .fa.fa-circle-o:before, .note-popover .fa.fa-circle-o:before { content: "\f26c"; } .note-editor .fa.fa-times:before, .note-popover .fa.fa-times:before { content: "\f136"; } .note-editor .note-air-popover .arrow, .note-popover .note-air-popover .arrow { left: 20px; } .note-editor { overflow: visible; border: 1px solid #e4e4e4; } .note-editor .note-editable { padding: 20px 23px; } .bootstrap-datetimepicker-widget { padding: 0 !important; margin: 0 !important; width: auto !important; } .bootstrap-datetimepicker-widget:after, .bootstrap-datetimepicker-widget:before { display: none !important; } .bootstrap-datetimepicker-widget table td { text-shadow: none; } .bootstrap-datetimepicker-widget table td span { margin: 0; } .bootstrap-datetimepicker-widget table td span:hover { background: transparent; } .bootstrap-datetimepicker-widget .glyphicon { font-family: 'Material-Design-Iconic-Font'; font-size: 18px; } .bootstrap-datetimepicker-widget .glyphicon-chevron-left:before { content: "\f2ff"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-right:before { content: "\f301"; } .bootstrap-datetimepicker-widget .glyphicon-time:before { content: "\f337"; } .bootstrap-datetimepicker-widget .glyphicon-calendar:before { content: "\f32e"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-up:before { content: "\f1e5"; } .bootstrap-datetimepicker-widget .glyphicon-chevron-down:before { content: "\f1e4"; } .bootstrap-datetimepicker-widget [data-action="togglePicker"] span { font-size: 25px; color: #ccc; } .bootstrap-datetimepicker-widget [data-action="togglePicker"] span:hover { color: #333; } .bootstrap-datetimepicker-widget a[data-action] { color: #009688; } .timepicker-picker .btn { box-shadow: none !important; } .timepicker-picker table tbody tr + tr:not(:last-child) { background: #009688; color: #fff; } .timepicker-picker table tbody tr + tr:not(:last-child) td { border-radius: 0; } .timepicker-picker .btn, .timepicker-picker .btn:hover { background: #fff; color: #333; } .datepicker.top { -webkit-transform-origin: 0 100% !important; -moz-transform-origin: 0 100% !important; -ms-transform-origin: 0 100% !important; transform-origin: 0 100% !important; } .datepicker table thead tr th { border-radius: 0; color: #fff; } .datepicker table thead tr th .glyphicon { width: 30px; height: 30px; border-radius: 50%; line-height: 29px; } .datepicker table thead tr th:hover .glyphicon { background: rgba(0, 0, 0, 0.2); } .datepicker table thead tr:first-child th { background: #009688; padding: 20px 0; } .datepicker table thead tr:first-child th:hover { background: #009688; } .datepicker table thead tr:first-child th.picker-switch { font-size: 16px; font-weight: 400; text-transform: uppercase; } .datepicker table thead tr:last-child th { text-transform: uppercase; font-weight: normal; font-size: 11px; } .datepicker table thead tr:last-child th:first-child { padding-left: 20px; } .datepicker table thead tr:last-child th:last-child { padding-right: 20px; } .datepicker table thead tr:last-child:not(:only-child) { background: #00877a; } .datepicker table tbody tr:last-child td { padding-bottom: 25px; } .datepicker table tbody tr td:first-child { padding-left: 13px; } .datepicker table tbody tr td:last-child { padding-right: 13px; } .datepicker table td.day { width: 35px; height: 35px; line-height: 20px; color: #333; position: relative; padding: 0; background: transparent; } .datepicker table td.day:hover { background: none; } .datepicker table td.day:before { content: ""; width: 35px; height: 35px; border-radius: 50%; margin-bottom: -33px; display: inline-block; background: transparent; position: static; text-shadow: none; } .datepicker table td.day.old, .datepicker table td.day.new { color: #CDCDCD; } .datepicker table td:not(.today):not(.active):hover:before { background: #F0F0F0; } .datepicker table td.today { color: #333; } .datepicker table td.today:before { background-color: #E2E2E2; } .datepicker table td.active { color: #fff; } .datepicker table td.active:before { background-color: #009688; } .datepicker-months .month, .datepicker-years .year, .timepicker-minutes .minute, .timepicker-hours .hour { border-radius: 50%; } .datepicker-months .month:not(.active):hover, .datepicker-years .year:not(.active):hover, .timepicker-minutes .minute:not(.active):hover, .timepicker-hours .hour:not(.active):hover { background: #F0F0F0; } .datepicker-months .month.active, .datepicker-years .year.active, .timepicker-minutes .minute.active, .timepicker-hours .hour.active { background: #009688; } .timepicker-minutes .minute, .timepicker-hours .hour { padding: 0; } .fileinput { position: relative; padding-right: 35px; } .fileinput .close { position: absolute; top: 5px; font-size: 12px; float: none; opacity: 1; font-weight: 500; border: 1px solid #ccc; width: 19px; text-align: center; height: 19px; line-height: 15px; border-radius: 50%; right: 0; } .fileinput .close:hover { background: #eee; } .fileinput .input-group-addon { padding: 0 10px; vertical-align: middle; } .fileinput .fileinput-preview { width: 200px; height: 150px; position: relative; } .fileinput .fileinput-preview img { display: inline-block; vertical-align: middle; margin-top: -13px; } .fileinput .fileinput-preview:after { content: ""; display: inline-block; vertical-align: middle; } #lg-slider:after { content: ""; -webkit-animation-fill-mode: both; animation-fill-mode: both; height: 50px; width: 50px; border-radius: 100%; border: 2px solid #2196f3; -webkit-animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -25px; z-index: -1; } #lg-outer { background: rgba(255, 255, 255, 0.95); } #lg-outer .object { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); border-radius: 2px; } #lg-close { display: none; } #lg-action { top: 0; width: 100%; left: 0; margin-left: 0 !important; height: 40px; text-align: center; } #lg-action > a { background: transparent; color: #9D9D9D; font-size: 18px; width: 28px; height: 37px; } #lg-action > a:hover { background: transparent; color: #000; } #lg-action .cl-thumb { position: fixed; right: 20px; bottom: 20px; width: 50px; height: 50px; border-radius: 50%; line-height: 38px; background: #f44336; -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } #lg-action .cl-thumb:after { text-align: center; left: 16px !important; bottom: 6px !important; color: #fff; } #lg-action .cl-thumb:hover { background: #f32c1e; } #lg-gallery .thumb-cont { background: #f44336; text-align: center; } #lg-gallery .thumb-cont .thumb-info { background: #f44336; } #lg-gallery .thumb-cont .thumb-info .count { display: none; } #lg-gallery .thumb-cont .thumb-info .close { width: 14px; margin-top: 0; background: none; } #lg-gallery .thumb-cont .thumb-info .close:hover { background: none; } #lg-gallery .thumb-cont .thumb { opacity: 1; filter: alpha(opacity=100); } #lg-gallery .thumb-cont .thumb-inner { display: inline-block; padding: 12px 12px 15px; } .lg-slide { background: none !important; } .lg-slide em { font-style: normal; } .lg-slide em h3 { margin-bottom: 5px; } .lg-slide .video-cont { box-shadow: 0 8px 17px rgba(0, 0, 0, 0.2), 0 6px 20px rgba(0, 0, 0, 0.19); } @-webkit-keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } @keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } .sweet-alert { border-radius: 2px; padding: 10px 30px; } .sweet-alert h2 { font-size: 16px; font-weight: 400; position: relative; z-index: 1; } .sweet-alert .lead { font-size: 13px; } .sweet-alert .btn { padding: 6px 12px; font-size: 13px; margin: 20px 2px 0; } .twitter-typeahead { width: 100%; } .twitter-typeahead .tt-menu { min-width: 200px; background: #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); display: block !important; z-index: 2 !important; -webkit-transform: scale(0); -ms-transform: scale(0); -o-transform: scale(0); transform: scale(0); opacity: 0; filter: alpha(opacity=0); -webkit-transition: all; -o-transition: all; transition: all; -webkit-transition-duration: 300ms; transition-duration: 300ms; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; -webkit-transform-origin: top left; -moz-transform-origin: top left; -ms-transform-origin: top left; transform-origin: top left; } .twitter-typeahead .tt-menu.tt-open:not(.tt-empty) { -webkit-transform: scale(1); -ms-transform: scale(1); -o-transform: scale(1); transform: scale(1); opacity: 1; filter: alpha(opacity=100); } .twitter-typeahead .tt-suggestion { padding: 8px 17px; color: #333; cursor: pointer; } .twitter-typeahead .tt-suggestion:hover, .twitter-typeahead .tt-cursor { background-color: rgba(0, 0, 0, 0.075); } .twitter-typeahead .tt-hint { color: #818181 !important; } .mCSB_scrollTools { width: 5px; } .mCSB_scrollTools .mCSB_dragger_bar { border-radius: 0 !important; } .mCSB_scrollTools.mCSB_scrollTools_horizontal, .mCSB_scrollTools.mCSB_scrollTools_vertical { margin: 0 !important; } .mCSB_scrollTools.mCSB_scrollTools_horizontal { height: 10px; } .mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar { background: rgba(0, 0, 0, 0.4); } .mCS-minimal-dark.mCSB_scrollTools_onDrag .mCSB_dragger .mCSB_dragger_bar { background: rgba(0, 0, 0, 0.5) !important; } .mCSB_inside > .mCSB_container { margin-right: 0; } ================================================ FILE: material-manage/src/main/webapp/static/css/font-awesome.css ================================================ /*! * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('../fonts/fontawesome-webfont.eot?v=4.4.0'); src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } .fa { display: inline-block; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* makes the font 33% larger relative to the icon container */ .fa-lg { font-size: 1.33333333em; line-height: 0.75em; vertical-align: -15%; } .fa-2x { font-size: 2em; } .fa-3x { font-size: 3em; } .fa-4x { font-size: 4em; } .fa-5x { font-size: 5em; } .fa-fw { width: 1.28571429em; text-align: center; } .fa-ul { padding-left: 0; margin-left: 2.14285714em; list-style-type: none; } .fa-ul > li { position: relative; } .fa-li { position: absolute; left: -2.14285714em; width: 2.14285714em; top: 0.14285714em; text-align: center; } .fa-li.fa-lg { left: -1.85714286em; } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eeeeee; border-radius: .1em; } .fa-pull-left { float: left; } .fa-pull-right { float: right; } .fa.fa-pull-left { margin-right: .3em; } .fa.fa-pull-right { margin-left: .3em; } /* Deprecated as of 4.4.0 */ .pull-right { float: right; } .pull-left { float: left; } .fa.pull-left { margin-right: .3em; } .fa.pull-right { margin-left: .3em; } .fa-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } .fa-pulse { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } .fa-rotate-90 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } .fa-rotate-180 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } .fa-rotate-270 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } .fa-flip-horizontal { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); -webkit-transform: scale(-1, 1); -ms-transform: scale(-1, 1); transform: scale(-1, 1); } .fa-flip-vertical { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); -webkit-transform: scale(1, -1); -ms-transform: scale(1, -1); transform: scale(1, -1); } :root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { filter: none; } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .fa-stack-1x { line-height: inherit; } .fa-stack-2x { font-size: 2em; } .fa-inverse { color: #ffffff; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .fa-glass:before { content: "\f000"; } .fa-music:before { content: "\f001"; } .fa-search:before { content: "\f002"; } .fa-envelope-o:before { content: "\f003"; } .fa-heart:before { content: "\f004"; } .fa-star:before { content: "\f005"; } .fa-star-o:before { content: "\f006"; } .fa-user:before { content: "\f007"; } .fa-film:before { content: "\f008"; } .fa-th-large:before { content: "\f009"; } .fa-th:before { content: "\f00a"; } .fa-th-list:before { content: "\f00b"; } .fa-check:before { content: "\f00c"; } .fa-remove:before, .fa-close:before, .fa-times:before { content: "\f00d"; } .fa-search-plus:before { content: "\f00e"; } .fa-search-minus:before { content: "\f010"; } .fa-power-off:before { content: "\f011"; } .fa-signal:before { content: "\f012"; } .fa-gear:before, .fa-cog:before { content: "\f013"; } .fa-trash-o:before { content: "\f014"; } .fa-home:before { content: "\f015"; } .fa-file-o:before { content: "\f016"; } .fa-clock-o:before { content: "\f017"; } .fa-road:before { content: "\f018"; } .fa-download:before { content: "\f019"; } .fa-arrow-circle-o-down:before { content: "\f01a"; } .fa-arrow-circle-o-up:before { content: "\f01b"; } .fa-inbox:before { content: "\f01c"; } .fa-play-circle-o:before { content: "\f01d"; } .fa-rotate-right:before, .fa-repeat:before { content: "\f01e"; } .fa-refresh:before { content: "\f021"; } .fa-list-alt:before { content: "\f022"; } .fa-lock:before { content: "\f023"; } .fa-flag:before { content: "\f024"; } .fa-headphones:before { content: "\f025"; } .fa-volume-off:before { content: "\f026"; } .fa-volume-down:before { content: "\f027"; } .fa-volume-up:before { content: "\f028"; } .fa-qrcode:before { content: "\f029"; } .fa-barcode:before { content: "\f02a"; } .fa-tag:before { content: "\f02b"; } .fa-tags:before { content: "\f02c"; } .fa-book:before { content: "\f02d"; } .fa-bookmark:before { content: "\f02e"; } .fa-print:before { content: "\f02f"; } .fa-camera:before { content: "\f030"; } .fa-font:before { content: "\f031"; } .fa-bold:before { content: "\f032"; } .fa-italic:before { content: "\f033"; } .fa-text-height:before { content: "\f034"; } .fa-text-width:before { content: "\f035"; } .fa-align-left:before { content: "\f036"; } .fa-align-center:before { content: "\f037"; } .fa-align-right:before { content: "\f038"; } .fa-align-justify:before { content: "\f039"; } .fa-list:before { content: "\f03a"; } .fa-dedent:before, .fa-outdent:before { content: "\f03b"; } .fa-indent:before { content: "\f03c"; } .fa-video-camera:before { content: "\f03d"; } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "\f03e"; } .fa-pencil:before { content: "\f040"; } .fa-map-marker:before { content: "\f041"; } .fa-adjust:before { content: "\f042"; } .fa-tint:before { content: "\f043"; } .fa-edit:before, .fa-pencil-square-o:before { content: "\f044"; } .fa-share-square-o:before { content: "\f045"; } .fa-check-square-o:before { content: "\f046"; } .fa-arrows:before { content: "\f047"; } .fa-step-backward:before { content: "\f048"; } .fa-fast-backward:before { content: "\f049"; } .fa-backward:before { content: "\f04a"; } .fa-play:before { content: "\f04b"; } .fa-pause:before { content: "\f04c"; } .fa-stop:before { content: "\f04d"; } .fa-forward:before { content: "\f04e"; } .fa-fast-forward:before { content: "\f050"; } .fa-step-forward:before { content: "\f051"; } .fa-eject:before { content: "\f052"; } .fa-chevron-left:before { content: "\f053"; } .fa-chevron-right:before { content: "\f054"; } .fa-plus-circle:before { content: "\f055"; } .fa-minus-circle:before { content: "\f056"; } .fa-times-circle:before { content: "\f057"; } .fa-check-circle:before { content: "\f058"; } .fa-question-circle:before { content: "\f059"; } .fa-info-circle:before { content: "\f05a"; } .fa-crosshairs:before { content: "\f05b"; } .fa-times-circle-o:before { content: "\f05c"; } .fa-check-circle-o:before { content: "\f05d"; } .fa-ban:before { content: "\f05e"; } .fa-arrow-left:before { content: "\f060"; } .fa-arrow-right:before { content: "\f061"; } .fa-arrow-up:before { content: "\f062"; } .fa-arrow-down:before { content: "\f063"; } .fa-mail-forward:before, .fa-share:before { content: "\f064"; } .fa-expand:before { content: "\f065"; } .fa-compress:before { content: "\f066"; } .fa-plus:before { content: "\f067"; } .fa-minus:before { content: "\f068"; } .fa-asterisk:before { content: "\f069"; } .fa-exclamation-circle:before { content: "\f06a"; } .fa-gift:before { content: "\f06b"; } .fa-leaf:before { content: "\f06c"; } .fa-fire:before { content: "\f06d"; } .fa-eye:before { content: "\f06e"; } .fa-eye-slash:before { content: "\f070"; } .fa-warning:before, .fa-exclamation-triangle:before { content: "\f071"; } .fa-plane:before { content: "\f072"; } .fa-calendar:before { content: "\f073"; } .fa-random:before { content: "\f074"; } .fa-comment:before { content: "\f075"; } .fa-magnet:before { content: "\f076"; } .fa-chevron-up:before { content: "\f077"; } .fa-chevron-down:before { content: "\f078"; } .fa-retweet:before { content: "\f079"; } .fa-shopping-cart:before { content: "\f07a"; } .fa-folder:before { content: "\f07b"; } .fa-folder-open:before { content: "\f07c"; } .fa-arrows-v:before { content: "\f07d"; } .fa-arrows-h:before { content: "\f07e"; } .fa-bar-chart-o:before, .fa-bar-chart:before { content: "\f080"; } .fa-twitter-square:before { content: "\f081"; } .fa-facebook-square:before { content: "\f082"; } .fa-camera-retro:before { content: "\f083"; } .fa-key:before { content: "\f084"; } .fa-gears:before, .fa-cogs:before { content: "\f085"; } .fa-comments:before { content: "\f086"; } .fa-thumbs-o-up:before { content: "\f087"; } .fa-thumbs-o-down:before { content: "\f088"; } .fa-star-half:before { content: "\f089"; } .fa-heart-o:before { content: "\f08a"; } .fa-sign-out:before { content: "\f08b"; } .fa-linkedin-square:before { content: "\f08c"; } .fa-thumb-tack:before { content: "\f08d"; } .fa-external-link:before { content: "\f08e"; } .fa-sign-in:before { content: "\f090"; } .fa-trophy:before { content: "\f091"; } .fa-github-square:before { content: "\f092"; } .fa-upload:before { content: "\f093"; } .fa-lemon-o:before { content: "\f094"; } .fa-phone:before { content: "\f095"; } .fa-square-o:before { content: "\f096"; } .fa-bookmark-o:before { content: "\f097"; } .fa-phone-square:before { content: "\f098"; } .fa-twitter:before { content: "\f099"; } .fa-facebook-f:before, .fa-facebook:before { content: "\f09a"; } .fa-github:before { content: "\f09b"; } .fa-unlock:before { content: "\f09c"; } .fa-credit-card:before { content: "\f09d"; } .fa-feed:before, .fa-rss:before { content: "\f09e"; } .fa-hdd-o:before { content: "\f0a0"; } .fa-bullhorn:before { content: "\f0a1"; } .fa-bell:before { content: "\f0f3"; } .fa-certificate:before { content: "\f0a3"; } .fa-hand-o-right:before { content: "\f0a4"; } .fa-hand-o-left:before { content: "\f0a5"; } .fa-hand-o-up:before { content: "\f0a6"; } .fa-hand-o-down:before { content: "\f0a7"; } .fa-arrow-circle-left:before { content: "\f0a8"; } .fa-arrow-circle-right:before { content: "\f0a9"; } .fa-arrow-circle-up:before { content: "\f0aa"; } .fa-arrow-circle-down:before { content: "\f0ab"; } .fa-globe:before { content: "\f0ac"; } .fa-wrench:before { content: "\f0ad"; } .fa-tasks:before { content: "\f0ae"; } .fa-filter:before { content: "\f0b0"; } .fa-briefcase:before { content: "\f0b1"; } .fa-arrows-alt:before { content: "\f0b2"; } .fa-group:before, .fa-users:before { content: "\f0c0"; } .fa-chain:before, .fa-link:before { content: "\f0c1"; } .fa-cloud:before { content: "\f0c2"; } .fa-flask:before { content: "\f0c3"; } .fa-cut:before, .fa-scissors:before { content: "\f0c4"; } .fa-copy:before, .fa-files-o:before { content: "\f0c5"; } .fa-paperclip:before { content: "\f0c6"; } .fa-save:before, .fa-floppy-o:before { content: "\f0c7"; } .fa-square:before { content: "\f0c8"; } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "\f0c9"; } .fa-list-ul:before { content: "\f0ca"; } .fa-list-ol:before { content: "\f0cb"; } .fa-strikethrough:before { content: "\f0cc"; } .fa-underline:before { content: "\f0cd"; } .fa-table:before { content: "\f0ce"; } .fa-magic:before { content: "\f0d0"; } .fa-truck:before { content: "\f0d1"; } .fa-pinterest:before { content: "\f0d2"; } .fa-pinterest-square:before { content: "\f0d3"; } .fa-google-plus-square:before { content: "\f0d4"; } .fa-google-plus:before { content: "\f0d5"; } .fa-money:before { content: "\f0d6"; } .fa-caret-down:before { content: "\f0d7"; } .fa-caret-up:before { content: "\f0d8"; } .fa-caret-left:before { content: "\f0d9"; } .fa-caret-right:before { content: "\f0da"; } .fa-columns:before { content: "\f0db"; } .fa-unsorted:before, .fa-sort:before { content: "\f0dc"; } .fa-sort-down:before, .fa-sort-desc:before { content: "\f0dd"; } .fa-sort-up:before, .fa-sort-asc:before { content: "\f0de"; } .fa-envelope:before { content: "\f0e0"; } .fa-linkedin:before { content: "\f0e1"; } .fa-rotate-left:before, .fa-undo:before { content: "\f0e2"; } .fa-legal:before, .fa-gavel:before { content: "\f0e3"; } .fa-dashboard:before, .fa-tachometer:before { content: "\f0e4"; } .fa-comment-o:before { content: "\f0e5"; } .fa-comments-o:before { content: "\f0e6"; } .fa-flash:before, .fa-bolt:before { content: "\f0e7"; } .fa-sitemap:before { content: "\f0e8"; } .fa-umbrella:before { content: "\f0e9"; } .fa-paste:before, .fa-clipboard:before { content: "\f0ea"; } .fa-lightbulb-o:before { content: "\f0eb"; } .fa-exchange:before { content: "\f0ec"; } .fa-cloud-download:before { content: "\f0ed"; } .fa-cloud-upload:before { content: "\f0ee"; } .fa-user-md:before { content: "\f0f0"; } .fa-stethoscope:before { content: "\f0f1"; } .fa-suitcase:before { content: "\f0f2"; } .fa-bell-o:before { content: "\f0a2"; } .fa-coffee:before { content: "\f0f4"; } .fa-cutlery:before { content: "\f0f5"; } .fa-file-text-o:before { content: "\f0f6"; } .fa-building-o:before { content: "\f0f7"; } .fa-hospital-o:before { content: "\f0f8"; } .fa-ambulance:before { content: "\f0f9"; } .fa-medkit:before { content: "\f0fa"; } .fa-fighter-jet:before { content: "\f0fb"; } .fa-beer:before { content: "\f0fc"; } .fa-h-square:before { content: "\f0fd"; } .fa-plus-square:before { content: "\f0fe"; } .fa-angle-double-left:before { content: "\f100"; } .fa-angle-double-right:before { content: "\f101"; } .fa-angle-double-up:before { content: "\f102"; } .fa-angle-double-down:before { content: "\f103"; } .fa-angle-left:before { content: "\f104"; } .fa-angle-right:before { content: "\f105"; } .fa-angle-up:before { content: "\f106"; } .fa-angle-down:before { content: "\f107"; } .fa-desktop:before { content: "\f108"; } .fa-laptop:before { content: "\f109"; } .fa-tablet:before { content: "\f10a"; } .fa-mobile-phone:before, .fa-mobile:before { content: "\f10b"; } .fa-circle-o:before { content: "\f10c"; } .fa-quote-left:before { content: "\f10d"; } .fa-quote-right:before { content: "\f10e"; } .fa-spinner:before { content: "\f110"; } .fa-circle:before { content: "\f111"; } .fa-mail-reply:before, .fa-reply:before { content: "\f112"; } .fa-github-alt:before { content: "\f113"; } .fa-folder-o:before { content: "\f114"; } .fa-folder-open-o:before { content: "\f115"; } .fa-smile-o:before { content: "\f118"; } .fa-frown-o:before { content: "\f119"; } .fa-meh-o:before { content: "\f11a"; } .fa-gamepad:before { content: "\f11b"; } .fa-keyboard-o:before { content: "\f11c"; } .fa-flag-o:before { content: "\f11d"; } .fa-flag-checkered:before { content: "\f11e"; } .fa-terminal:before { content: "\f120"; } .fa-code:before { content: "\f121"; } .fa-mail-reply-all:before, .fa-reply-all:before { content: "\f122"; } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "\f123"; } .fa-location-arrow:before { content: "\f124"; } .fa-crop:before { content: "\f125"; } .fa-code-fork:before { content: "\f126"; } .fa-unlink:before, .fa-chain-broken:before { content: "\f127"; } .fa-question:before { content: "\f128"; } .fa-info:before { content: "\f129"; } .fa-exclamation:before { content: "\f12a"; } .fa-superscript:before { content: "\f12b"; } .fa-subscript:before { content: "\f12c"; } .fa-eraser:before { content: "\f12d"; } .fa-puzzle-piece:before { content: "\f12e"; } .fa-microphone:before { content: "\f130"; } .fa-microphone-slash:before { content: "\f131"; } .fa-shield:before { content: "\f132"; } .fa-calendar-o:before { content: "\f133"; } .fa-fire-extinguisher:before { content: "\f134"; } .fa-rocket:before { content: "\f135"; } .fa-maxcdn:before { content: "\f136"; } .fa-chevron-circle-left:before { content: "\f137"; } .fa-chevron-circle-right:before { content: "\f138"; } .fa-chevron-circle-up:before { content: "\f139"; } .fa-chevron-circle-down:before { content: "\f13a"; } .fa-html5:before { content: "\f13b"; } .fa-css3:before { content: "\f13c"; } .fa-anchor:before { content: "\f13d"; } .fa-unlock-alt:before { content: "\f13e"; } .fa-bullseye:before { content: "\f140"; } .fa-ellipsis-h:before { content: "\f141"; } .fa-ellipsis-v:before { content: "\f142"; } .fa-rss-square:before { content: "\f143"; } .fa-play-circle:before { content: "\f144"; } .fa-ticket:before { content: "\f145"; } .fa-minus-square:before { content: "\f146"; } .fa-minus-square-o:before { content: "\f147"; } .fa-level-up:before { content: "\f148"; } .fa-level-down:before { content: "\f149"; } .fa-check-square:before { content: "\f14a"; } .fa-pencil-square:before { content: "\f14b"; } .fa-external-link-square:before { content: "\f14c"; } .fa-share-square:before { content: "\f14d"; } .fa-compass:before { content: "\f14e"; } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "\f150"; } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "\f151"; } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "\f152"; } .fa-euro:before, .fa-eur:before { content: "\f153"; } .fa-gbp:before { content: "\f154"; } .fa-dollar:before, .fa-usd:before { content: "\f155"; } .fa-rupee:before, .fa-inr:before { content: "\f156"; } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "\f157"; } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "\f158"; } .fa-won:before, .fa-krw:before { content: "\f159"; } .fa-bitcoin:before, .fa-btc:before { content: "\f15a"; } .fa-file:before { content: "\f15b"; } .fa-file-text:before { content: "\f15c"; } .fa-sort-alpha-asc:before { content: "\f15d"; } .fa-sort-alpha-desc:before { content: "\f15e"; } .fa-sort-amount-asc:before { content: "\f160"; } .fa-sort-amount-desc:before { content: "\f161"; } .fa-sort-numeric-asc:before { content: "\f162"; } .fa-sort-numeric-desc:before { content: "\f163"; } .fa-thumbs-up:before { content: "\f164"; } .fa-thumbs-down:before { content: "\f165"; } .fa-youtube-square:before { content: "\f166"; } .fa-youtube:before { content: "\f167"; } .fa-xing:before { content: "\f168"; } .fa-xing-square:before { content: "\f169"; } .fa-youtube-play:before { content: "\f16a"; } .fa-dropbox:before { content: "\f16b"; } .fa-stack-overflow:before { content: "\f16c"; } .fa-instagram:before { content: "\f16d"; } .fa-flickr:before { content: "\f16e"; } .fa-adn:before { content: "\f170"; } .fa-bitbucket:before { content: "\f171"; } .fa-bitbucket-square:before { content: "\f172"; } .fa-tumblr:before { content: "\f173"; } .fa-tumblr-square:before { content: "\f174"; } .fa-long-arrow-down:before { content: "\f175"; } .fa-long-arrow-up:before { content: "\f176"; } .fa-long-arrow-left:before { content: "\f177"; } .fa-long-arrow-right:before { content: "\f178"; } .fa-apple:before { content: "\f179"; } .fa-windows:before { content: "\f17a"; } .fa-android:before { content: "\f17b"; } .fa-linux:before { content: "\f17c"; } .fa-dribbble:before { content: "\f17d"; } .fa-skype:before { content: "\f17e"; } .fa-foursquare:before { content: "\f180"; } .fa-trello:before { content: "\f181"; } .fa-female:before { content: "\f182"; } .fa-male:before { content: "\f183"; } .fa-gittip:before, .fa-gratipay:before { content: "\f184"; } .fa-sun-o:before { content: "\f185"; } .fa-moon-o:before { content: "\f186"; } .fa-archive:before { content: "\f187"; } .fa-bug:before { content: "\f188"; } .fa-vk:before { content: "\f189"; } .fa-weibo:before { content: "\f18a"; } .fa-renren:before { content: "\f18b"; } .fa-pagelines:before { content: "\f18c"; } .fa-stack-exchange:before { content: "\f18d"; } .fa-arrow-circle-o-right:before { content: "\f18e"; } .fa-arrow-circle-o-left:before { content: "\f190"; } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "\f191"; } .fa-dot-circle-o:before { content: "\f192"; } .fa-wheelchair:before { content: "\f193"; } .fa-vimeo-square:before { content: "\f194"; } .fa-turkish-lira:before, .fa-try:before { content: "\f195"; } .fa-plus-square-o:before { content: "\f196"; } .fa-space-shuttle:before { content: "\f197"; } .fa-slack:before { content: "\f198"; } .fa-envelope-square:before { content: "\f199"; } .fa-wordpress:before { content: "\f19a"; } .fa-openid:before { content: "\f19b"; } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "\f19c"; } .fa-mortar-board:before, .fa-graduation-cap:before { content: "\f19d"; } .fa-yahoo:before { content: "\f19e"; } .fa-google:before { content: "\f1a0"; } .fa-reddit:before { content: "\f1a1"; } .fa-reddit-square:before { content: "\f1a2"; } .fa-stumbleupon-circle:before { content: "\f1a3"; } .fa-stumbleupon:before { content: "\f1a4"; } .fa-delicious:before { content: "\f1a5"; } .fa-digg:before { content: "\f1a6"; } .fa-pied-piper:before { content: "\f1a7"; } .fa-pied-piper-alt:before { content: "\f1a8"; } .fa-drupal:before { content: "\f1a9"; } .fa-joomla:before { content: "\f1aa"; } .fa-language:before { content: "\f1ab"; } .fa-fax:before { content: "\f1ac"; } .fa-building:before { content: "\f1ad"; } .fa-child:before { content: "\f1ae"; } .fa-paw:before { content: "\f1b0"; } .fa-spoon:before { content: "\f1b1"; } .fa-cube:before { content: "\f1b2"; } .fa-cubes:before { content: "\f1b3"; } .fa-behance:before { content: "\f1b4"; } .fa-behance-square:before { content: "\f1b5"; } .fa-steam:before { content: "\f1b6"; } .fa-steam-square:before { content: "\f1b7"; } .fa-recycle:before { content: "\f1b8"; } .fa-automobile:before, .fa-car:before { content: "\f1b9"; } .fa-cab:before, .fa-taxi:before { content: "\f1ba"; } .fa-tree:before { content: "\f1bb"; } .fa-spotify:before { content: "\f1bc"; } .fa-deviantart:before { content: "\f1bd"; } .fa-soundcloud:before { content: "\f1be"; } .fa-database:before { content: "\f1c0"; } .fa-file-pdf-o:before { content: "\f1c1"; } .fa-file-word-o:before { content: "\f1c2"; } .fa-file-excel-o:before { content: "\f1c3"; } .fa-file-powerpoint-o:before { content: "\f1c4"; } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "\f1c5"; } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "\f1c6"; } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "\f1c7"; } .fa-file-movie-o:before, .fa-file-video-o:before { content: "\f1c8"; } .fa-file-code-o:before { content: "\f1c9"; } .fa-vine:before { content: "\f1ca"; } .fa-codepen:before { content: "\f1cb"; } .fa-jsfiddle:before { content: "\f1cc"; } .fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "\f1cd"; } .fa-circle-o-notch:before { content: "\f1ce"; } .fa-ra:before, .fa-rebel:before { content: "\f1d0"; } .fa-ge:before, .fa-empire:before { content: "\f1d1"; } .fa-git-square:before { content: "\f1d2"; } .fa-git:before { content: "\f1d3"; } .fa-y-combinator-square:before, .fa-yc-square:before, .fa-hacker-news:before { content: "\f1d4"; } .fa-tencent-weibo:before { content: "\f1d5"; } .fa-qq:before { content: "\f1d6"; } .fa-wechat:before, .fa-weixin:before { content: "\f1d7"; } .fa-send:before, .fa-paper-plane:before { content: "\f1d8"; } .fa-send-o:before, .fa-paper-plane-o:before { content: "\f1d9"; } .fa-history:before { content: "\f1da"; } .fa-circle-thin:before { content: "\f1db"; } .fa-header:before { content: "\f1dc"; } .fa-paragraph:before { content: "\f1dd"; } .fa-sliders:before { content: "\f1de"; } .fa-share-alt:before { content: "\f1e0"; } .fa-share-alt-square:before { content: "\f1e1"; } .fa-bomb:before { content: "\f1e2"; } .fa-soccer-ball-o:before, .fa-futbol-o:before { content: "\f1e3"; } .fa-tty:before { content: "\f1e4"; } .fa-binoculars:before { content: "\f1e5"; } .fa-plug:before { content: "\f1e6"; } .fa-slideshare:before { content: "\f1e7"; } .fa-twitch:before { content: "\f1e8"; } .fa-yelp:before { content: "\f1e9"; } .fa-newspaper-o:before { content: "\f1ea"; } .fa-wifi:before { content: "\f1eb"; } .fa-calculator:before { content: "\f1ec"; } .fa-paypal:before { content: "\f1ed"; } .fa-google-wallet:before { content: "\f1ee"; } .fa-cc-visa:before { content: "\f1f0"; } .fa-cc-mastercard:before { content: "\f1f1"; } .fa-cc-discover:before { content: "\f1f2"; } .fa-cc-amex:before { content: "\f1f3"; } .fa-cc-paypal:before { content: "\f1f4"; } .fa-cc-stripe:before { content: "\f1f5"; } .fa-bell-slash:before { content: "\f1f6"; } .fa-bell-slash-o:before { content: "\f1f7"; } .fa-trash:before { content: "\f1f8"; } .fa-copyright:before { content: "\f1f9"; } .fa-at:before { content: "\f1fa"; } .fa-eyedropper:before { content: "\f1fb"; } .fa-paint-brush:before { content: "\f1fc"; } .fa-birthday-cake:before { content: "\f1fd"; } .fa-area-chart:before { content: "\f1fe"; } .fa-pie-chart:before { content: "\f200"; } .fa-line-chart:before { content: "\f201"; } .fa-lastfm:before { content: "\f202"; } .fa-lastfm-square:before { content: "\f203"; } .fa-toggle-off:before { content: "\f204"; } .fa-toggle-on:before { content: "\f205"; } .fa-bicycle:before { content: "\f206"; } .fa-bus:before { content: "\f207"; } .fa-ioxhost:before { content: "\f208"; } .fa-angellist:before { content: "\f209"; } .fa-cc:before { content: "\f20a"; } .fa-shekel:before, .fa-sheqel:before, .fa-ils:before { content: "\f20b"; } .fa-meanpath:before { content: "\f20c"; } .fa-buysellads:before { content: "\f20d"; } .fa-connectdevelop:before { content: "\f20e"; } .fa-dashcube:before { content: "\f210"; } .fa-forumbee:before { content: "\f211"; } .fa-leanpub:before { content: "\f212"; } .fa-sellsy:before { content: "\f213"; } .fa-shirtsinbulk:before { content: "\f214"; } .fa-simplybuilt:before { content: "\f215"; } .fa-skyatlas:before { content: "\f216"; } .fa-cart-plus:before { content: "\f217"; } .fa-cart-arrow-down:before { content: "\f218"; } .fa-diamond:before { content: "\f219"; } .fa-ship:before { content: "\f21a"; } .fa-user-secret:before { content: "\f21b"; } .fa-motorcycle:before { content: "\f21c"; } .fa-street-view:before { content: "\f21d"; } .fa-heartbeat:before { content: "\f21e"; } .fa-venus:before { content: "\f221"; } .fa-mars:before { content: "\f222"; } .fa-mercury:before { content: "\f223"; } .fa-intersex:before, .fa-transgender:before { content: "\f224"; } .fa-transgender-alt:before { content: "\f225"; } .fa-venus-double:before { content: "\f226"; } .fa-mars-double:before { content: "\f227"; } .fa-venus-mars:before { content: "\f228"; } .fa-mars-stroke:before { content: "\f229"; } .fa-mars-stroke-v:before { content: "\f22a"; } .fa-mars-stroke-h:before { content: "\f22b"; } .fa-neuter:before { content: "\f22c"; } .fa-genderless:before { content: "\f22d"; } .fa-facebook-official:before { content: "\f230"; } .fa-pinterest-p:before { content: "\f231"; } .fa-whatsapp:before { content: "\f232"; } .fa-server:before { content: "\f233"; } .fa-user-plus:before { content: "\f234"; } .fa-user-times:before { content: "\f235"; } .fa-hotel:before, .fa-bed:before { content: "\f236"; } .fa-viacoin:before { content: "\f237"; } .fa-train:before { content: "\f238"; } .fa-subway:before { content: "\f239"; } .fa-medium:before { content: "\f23a"; } .fa-yc:before, .fa-y-combinator:before { content: "\f23b"; } .fa-optin-monster:before { content: "\f23c"; } .fa-opencart:before { content: "\f23d"; } .fa-expeditedssl:before { content: "\f23e"; } .fa-battery-4:before, .fa-battery-full:before { content: "\f240"; } .fa-battery-3:before, .fa-battery-three-quarters:before { content: "\f241"; } .fa-battery-2:before, .fa-battery-half:before { content: "\f242"; } .fa-battery-1:before, .fa-battery-quarter:before { content: "\f243"; } .fa-battery-0:before, .fa-battery-empty:before { content: "\f244"; } .fa-mouse-pointer:before { content: "\f245"; } .fa-i-cursor:before { content: "\f246"; } .fa-object-group:before { content: "\f247"; } .fa-object-ungroup:before { content: "\f248"; } .fa-sticky-note:before { content: "\f249"; } .fa-sticky-note-o:before { content: "\f24a"; } .fa-cc-jcb:before { content: "\f24b"; } .fa-cc-diners-club:before { content: "\f24c"; } .fa-clone:before { content: "\f24d"; } .fa-balance-scale:before { content: "\f24e"; } .fa-hourglass-o:before { content: "\f250"; } .fa-hourglass-1:before, .fa-hourglass-start:before { content: "\f251"; } .fa-hourglass-2:before, .fa-hourglass-half:before { content: "\f252"; } .fa-hourglass-3:before, .fa-hourglass-end:before { content: "\f253"; } .fa-hourglass:before { content: "\f254"; } .fa-hand-grab-o:before, .fa-hand-rock-o:before { content: "\f255"; } .fa-hand-stop-o:before, .fa-hand-paper-o:before { content: "\f256"; } .fa-hand-scissors-o:before { content: "\f257"; } .fa-hand-lizard-o:before { content: "\f258"; } .fa-hand-spock-o:before { content: "\f259"; } .fa-hand-pointer-o:before { content: "\f25a"; } .fa-hand-peace-o:before { content: "\f25b"; } .fa-trademark:before { content: "\f25c"; } .fa-registered:before { content: "\f25d"; } .fa-creative-commons:before { content: "\f25e"; } .fa-gg:before { content: "\f260"; } .fa-gg-circle:before { content: "\f261"; } .fa-tripadvisor:before { content: "\f262"; } .fa-odnoklassniki:before { content: "\f263"; } .fa-odnoklassniki-square:before { content: "\f264"; } .fa-get-pocket:before { content: "\f265"; } .fa-wikipedia-w:before { content: "\f266"; } .fa-safari:before { content: "\f267"; } .fa-chrome:before { content: "\f268"; } .fa-firefox:before { content: "\f269"; } .fa-opera:before { content: "\f26a"; } .fa-internet-explorer:before { content: "\f26b"; } .fa-tv:before, .fa-television:before { content: "\f26c"; } .fa-contao:before { content: "\f26d"; } .fa-500px:before { content: "\f26e"; } .fa-amazon:before { content: "\f270"; } .fa-calendar-plus-o:before { content: "\f271"; } .fa-calendar-minus-o:before { content: "\f272"; } .fa-calendar-times-o:before { content: "\f273"; } .fa-calendar-check-o:before { content: "\f274"; } .fa-industry:before { content: "\f275"; } .fa-map-pin:before { content: "\f276"; } .fa-map-signs:before { content: "\f277"; } .fa-map-o:before { content: "\f278"; } .fa-map:before { content: "\f279"; } .fa-commenting:before { content: "\f27a"; } .fa-commenting-o:before { content: "\f27b"; } .fa-houzz:before { content: "\f27c"; } .fa-vimeo:before { content: "\f27d"; } .fa-black-tie:before { content: "\f27e"; } .fa-fonticons:before { content: "\f280"; } ================================================ FILE: material-manage/src/main/webapp/static/css/material.css ================================================ .input-group .form-control{ float:none; } .ibox-title h5 { display: inline-block; font-size: 14px; margin: 0 0 7px; padding: 0; text-overflow: ellipsis; float: left; font-weight:600; margin-bottom:15px; } .dept-tree{ padding:10px 0px 0px 0px; } .ztree{ padding: 0px;margin: 5px; } .panel-ztree { box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.15); min-height: 644px; } .fixed-table-toolbar>div>div>.col-sm-6:first-child{ padding-left:0px; } .fixed-table-toolbar>div>div>div:first-child{ padding-left:0px; } .bs-checkbox { text-align: center !important; vertical-align: middle !important; } .fixed-table-pagination .pagination a { padding: 9px 12px !important; } ================================================ FILE: material-manage/src/main/webapp/static/css/plugins/dataTables/dataTables.bootstrap.css ================================================ div.dataTables_length label { float: left; text-align: left; font-weight: normal; } div.dataTables_length select { width: 75px; } div.dataTables_filter label { float: right; font-weight: normal; } div.dataTables_filter input { width: 16em; } div.dataTables_info { padding-top: 8px; } div.dataTables_paginate { float: right; margin: 0; } div.dataTables_paginate ul.pagination { margin: 2px 0; white-space: nowrap; } table.dataTable, table.dataTable td, table.dataTable th { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } table.dataTable { clear: both; margin-top: 6px !important; margin-bottom: 6px !important; max-width: none !important; } table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_desc_disabled { cursor: pointer; } table.dataTable thead .sorting { } table.dataTable thead .sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; } table.dataTable thead .sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; } table.dataTable thead .sorting_asc_disabled { } table.dataTable thead .sorting_desc_disabled { } table.dataTable th:active { outline: none; } /* Scrolling */ div.dataTables_scrollHead table { margin-bottom: 0 !important; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } div.dataTables_scrollHead table thead tr:last-child th:first-child, div.dataTables_scrollHead table thead tr:last-child td:first-child { border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; } div.dataTables_scrollBody table { margin-top: 0 !important; margin-bottom: 0 !important; border-top: none; } div.dataTables_scrollBody tbody tr:first-child th, div.dataTables_scrollBody tbody tr:first-child td { border-top: none; } div.dataTables_scrollFoot table { margin-top: 0 !important; border-top: none; } /* * TableTools styles */ .table tbody tr.active td, .table tbody tr.active th { color: white; background-color: #08C; } .table tbody tr.active:hover td, .table tbody tr.active:hover th { background-color: #0075b0 !important; } .table tbody tr.active a { color: white; } .table-striped tbody tr.active:nth-child(odd) td, .table-striped tbody tr.active:nth-child(odd) th { background-color: #017ebc; } table.DTTT_selectable tbody tr { cursor: pointer; } div.DTTT .btn { font-size: 12px; color: #333 !important; } div.DTTT .btn:hover { text-decoration: none !important; } ul.DTTT_dropdown.dropdown-menu { z-index: 2003; } ul.DTTT_dropdown.dropdown-menu a { color: #333 !important; /* needed only when demo_page.css is included */ } ul.DTTT_dropdown.dropdown-menu li { position: relative; } ul.DTTT_dropdown.dropdown-menu li:hover a { color: white !important; background-color: #0088cc; } div.DTTT_collection_background { z-index: 2002; } /* TableTools information display */ div.DTTT_print_info.modal { height: 150px; margin-top: -75px; text-align: center; } div.DTTT_print_info h6 { margin: 1em; font-size: 28px; font-weight: normal; line-height: 28px; } div.DTTT_print_info p { font-size: 14px; line-height: 20px; } /* * FixedColumns styles */ div.DTFC_LeftHeadWrapper table, div.DTFC_LeftFootWrapper table, div.DTFC_RightHeadWrapper table, div.DTFC_RightFootWrapper table, table.DTFC_Cloned tr.even { background-color: white; } div.DTFC_RightHeadWrapper table, div.DTFC_LeftHeadWrapper table { margin-bottom: 0 !important; border-top-right-radius: 0 !important; border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; } div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child, div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child, div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; } div.DTFC_RightBodyWrapper table, div.DTFC_LeftBodyWrapper table { margin-bottom: 0 !important; border-top: none; } div.DTFC_RightBodyWrapper tbody tr:first-child th, div.DTFC_RightBodyWrapper tbody tr:first-child td, div.DTFC_LeftBodyWrapper tbody tr:first-child th, div.DTFC_LeftBodyWrapper tbody tr:first-child td { border-top: none; } div.DTFC_RightFootWrapper table, div.DTFC_LeftFootWrapper table { border-top: none; } ================================================ FILE: material-manage/src/main/webapp/static/css/plugins/jquery-treegrid/css/jquery.treegrid.css ================================================ .treegrid-indent {width:16px; height: 16px; display: inline-block; position: relative;} .treegrid-expander {width:16px; height: 16px; display: inline-block; position: relative; cursor: pointer;} .treegrid-expander-expanded{background-image: url(../img/collapse.png); } .treegrid-expander-collapsed{background-image: url(../img/expand.png);} .treegrid-selected{background: #f5f5f5 !important;} .treegrid-table{border:0 !important;margin-bottom:0} .treegrid-table tbody {display:block;height:auto;overflow-y:auto;} .treegrid-table thead, .treegrid-table tbody tr {display:table;width:100%;table-layout:fixed;} .treegrid-thead th{line-height:40px;border: 0 !important;background:#fff !important;border-radius: 4px;border-left:1px solid #e7eaec !important;border-bottom:2px solid #e7eaec !important;text-align: center;} .treegrid-thead tr :first-child{border-left:0 !important} .treegrid-tbody td{border: 0 !important;border-left:1px solid #e7eaec !important;border-bottom:1px solid #e7eaec !important;} .treegrid-tbody tr :first-child{border-left:0 !important} ================================================ FILE: material-manage/src/main/webapp/static/css/plugins/webuploader/webuploader.css ================================================ .webuploader-container { position: relative; } .webuploader-element-invisible { position: absolute !important; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px,1px,1px,1px); } .webuploader-pick { position: relative; display: inline-block; cursor: pointer; background: #1ab394; padding: 6px 12px; color: #fff; text-align: center; border-radius: 3px; overflow: hidden; font-size: 14px; font-weight: 400; } .webuploader-pick-hover { background: #18ab8d; } .webuploader-pick-disable { opacity: 0.6; pointer-events:none; } ================================================ FILE: material-manage/src/main/webapp/static/css/plugins/ztree/demo.css ================================================ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0;padding: 0;border: 0;outline: 0;font-weight: inherit;font-style: inherit;font-size: 100%;font-family: inherit;vertical-align: baseline;} body {color: #2f332a;font: 15px/21px Arial, Helvetica, simsun, sans-serif;background: #f0f6e4 \9;} h1, h2, h3, h4, h5, h6 {color: #2f332a;font-weight: bold;font-family: Helvetica, Arial, sans-serif;padding-bottom: 5px;} h1 {font-size: 24px;line-height: 34px;text-align: center;} h2 {font-size: 14px;line-height: 24px;padding-top: 5px;} h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;} a {color:#3C6E31;text-decoration: underline;} a:hover {background-color:#3C6E31;color:white;} input.radio {margin: 0 2px 0 8px;} input.radio.first {margin-left:0;} input.empty {color: lightgray;} code {color: #2f332a;} .highlight_red {color:#A60000;} .highlight_green {color:#A7F43D;} li {list-style: circle;font-size: 12px;} li.title {list-style: none;} ul.list {margin-left: 17px;} div.content_wrap {width: 600px;height:380px;} div.content_wrap div.left{float: left;width: 250px;} div.content_wrap div.right{float: right;width: 340px;} div.zTreeDemoBackground {width:250px;height:362px;text-align:left;} ul.ztree {margin-top: 10px;border: 1px solid #617775;background: #f0f6e4;width:220px;height:360px;overflow-y:scroll;overflow-x:auto;} ul.log {border: 1px solid #617775;background: #f0f6e4;width:300px;height:170px;overflow: hidden;} ul.log.small {height:45px;} ul.log li {color: #666666;list-style: none;padding-left: 10px;} ul.log li.dark {background-color: #E3E3E3;} /* ruler */ div.ruler {height:20px; width:220px; background-color:#f0f6e4;border: 1px solid #333; margin-bottom: 5px; cursor: pointer} div.ruler div.cursor {height:20px; width:30px; background-color:#3C6E31; color:white; text-align: right; padding-right: 5px; cursor: pointer} ================================================ FILE: material-manage/src/main/webapp/static/css/plugins/ztree/zTreeStyle.css ================================================ /*------------------------------------- zTree Style version: 3.5.19 author: Hunter.z email: hunter.z@263.net website: http://code.google.com/p/jquerytree/ -------------------------------------*/ .ztree * {padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif} .ztree {margin:0; padding:5px; color:#333} .ztree li{padding:0; margin:0; list-style:none; line-height:14px; text-align:left; white-space:nowrap; outline:0} .ztree li ul{ margin:0; padding:0 0 0 18px} .ztree li ul.line{ background:url(./img/line_conn.gif) 0 0 repeat-y;} .ztree li a {padding:1px 3px 0 0; margin:0; cursor:pointer; height:17px; color:#333; background-color: transparent; text-decoration:none; vertical-align:top; display: inline-block} .ztree li a:hover {text-decoration:underline} .ztree li a.curSelectedNode {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;} .ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;} .ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#316AC5; color:white; height:16px; border:1px #316AC5 solid; opacity:0.8; filter:alpha(opacity=80)} .ztree li a.tmpTargetNode_prev {} .ztree li a.tmpTargetNode_next {} .ztree li a input.rename {height:14px; width:80px; padding:0; margin:0; font-size:12px; border:1px #7EC4CC solid; *border:0px} .ztree li span {line-height:16px; margin-right:2px} .ztree li span.button {line-height:0; margin:0; width:16px; height:16px; display: inline-block; vertical-align:middle; border:0 none; cursor: pointer;outline:none; background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")} .ztree li span.button.chk {width:13px; height:13px; margin:0 3px 0 0; cursor: auto} .ztree li span.button.chk.checkbox_false_full {background-position:0 0} .ztree li span.button.chk.checkbox_false_full_focus {background-position:0 -14px} .ztree li span.button.chk.checkbox_false_part {background-position:0 -28px} .ztree li span.button.chk.checkbox_false_part_focus {background-position:0 -42px} .ztree li span.button.chk.checkbox_false_disable {background-position:0 -56px} .ztree li span.button.chk.checkbox_true_full {background-position:-14px 0} .ztree li span.button.chk.checkbox_true_full_focus {background-position:-14px -14px} .ztree li span.button.chk.checkbox_true_part {background-position:-14px -28px} .ztree li span.button.chk.checkbox_true_part_focus {background-position:-14px -42px} .ztree li span.button.chk.checkbox_true_disable {background-position:-14px -56px} .ztree li span.button.chk.radio_false_full {background-position:-28px 0} .ztree li span.button.chk.radio_false_full_focus {background-position:-28px -14px} .ztree li span.button.chk.radio_false_part {background-position:-28px -28px} .ztree li span.button.chk.radio_false_part_focus {background-position:-28px -42px} .ztree li span.button.chk.radio_false_disable {background-position:-28px -56px} .ztree li span.button.chk.radio_true_full {background-position:-42px 0} .ztree li span.button.chk.radio_true_full_focus {background-position:-42px -14px} .ztree li span.button.chk.radio_true_part {background-position:-42px -28px} .ztree li span.button.chk.radio_true_part_focus {background-position:-42px -42px} .ztree li span.button.chk.radio_true_disable {background-position:-42px -56px} .ztree li span.button.switch {width:18px; height:18px} .ztree li span.button.root_open{background-position:-92px -54px} .ztree li span.button.root_close{background-position:-74px -54px} .ztree li span.button.roots_open{background-position:-92px 0} .ztree li span.button.roots_close{background-position:-74px 0} .ztree li span.button.center_open{background-position:-92px -18px} .ztree li span.button.center_close{background-position:-74px -18px} .ztree li span.button.bottom_open{background-position:-92px -36px} .ztree li span.button.bottom_close{background-position:-74px -36px} .ztree li span.button.noline_open{background-position:-92px -72px} .ztree li span.button.noline_close{background-position:-74px -72px} .ztree li span.button.root_docu{ background:none;} .ztree li span.button.roots_docu{background-position:-56px 0} .ztree li span.button.center_docu{background-position:-56px -18px} .ztree li span.button.bottom_docu{background-position:-56px -36px} .ztree li span.button.noline_docu{ background:none;} .ztree li span.button.ico_open{margin-right:2px; background-position:-110px -16px; vertical-align:top; *vertical-align:middle} .ztree li span.button.ico_close{margin-right:2px; background-position:-110px 0; vertical-align:top; *vertical-align:middle} .ztree li span.button.ico_docu{margin-right:2px; background-position:-110px -32px; vertical-align:top; *vertical-align:middle} .ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle} .ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle} .ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle} ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)} span.tmpzTreeMove_arrow {width:16px; height:16px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute; background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; background-position:-110px -80px; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")} ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)} .zTreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute} /* level style*/ /*.ztree li span.button.level0 { display:none; } .ztree li ul.level0 { padding:0; background:none; }*/ ================================================ FILE: material-manage/src/main/webapp/static/js/charts.js ================================================ $(document).ready(function () { /* * SPARKLINE */ function sparklineBar(id, values, height, barWidth, barColor, barSpacing) { $('.'+id).sparkline(values, { type: 'bar', height: height, barWidth: barWidth, barColor: barColor, barSpacing: barSpacing }) } function sparklineLine(id, values, width, height, lineColor, fillColor, lineWidth, maxSpotColor, minSpotColor, spotColor, spotRadius, hSpotColor, hLineColor) { $('.'+id).sparkline(values, { type: 'line', width: width, height: height, lineColor: lineColor, fillColor: fillColor, lineWidth: lineWidth, maxSpotColor: maxSpotColor, minSpotColor: minSpotColor, spotColor: spotColor, spotRadius: spotRadius, highlightSpotColor: hSpotColor, highlightLineColor: hLineColor }); } function sparklinePie(id, values, width, height, sliceColors) { $('.'+id).sparkline(values, { type: 'pie', width: width, height: height, sliceColors: sliceColors, offset: 0, borderWidth: 0 }); } /* Mini Chart - Bar Chart 1 */ if ($('.stats-bar')[0]) { sparklineBar('stats-bar', [6,4,8,6,5,6,7,8,3,5,9,5,8,4,3,6,8], '45px', 3, '#fff', 2); } /* Mini Chart - Bar Chart 2 */ if ($('.stats-bar-2')[0]) { sparklineBar('stats-bar-2', [4,7,6,2,5,3,8,6,6,4,8,6,5,8,2,4,6], '45px', 3, '#fff', 2); } /* Mini Chart - Line Chart 1 */ if ($('.stats-line')[0]) { sparklineLine('stats-line', [9,4,6,5,6,4,5,7,9,3,6,5], 85, 45, '#fff', 'rgba(0,0,0,0)', 1.25, 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 3, '#fff', 'rgba(255,255,255,0.4)'); } /* Mini Chart - Line Chart 2 */ if ($('.stats-line-2')[0]) { sparklineLine('stats-line-2', [5,6,3,9,7,5,4,6,5,6,4,9], 85, 45, '#fff', 'rgba(0,0,0,0)', 1.25, 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 3, '#fff', 'rgba(255,255,255,0.4)'); } /* Mini Chart - Pie Chart 1 */ if ($('.stats-pie')[0]) { sparklinePie('stats-pie', [20, 35, 30, 5], 45, 45, ['#fff', 'rgba(255,255,255,0.7)', 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.2)']); } /* Dash Widget Line Chart */ if ($('.dash-widget-visits')[0]) { sparklineLine('dash-widget-visits', [9,4,6,5,6,4,5,7,9,3,6,5], '100%', '95px', 'rgba(255,255,255,0.7)', 'rgba(0,0,0,0)', 2, 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.4)', 5, 'rgba(255,255,255,0.4)', '#fff'); } /* * Easy Pie Charts - Used in widgets */ function easyPieChart(id, trackColor, scaleColor, barColor, lineWidth, lineCap, size) { $('.'+id).easyPieChart({ trackColor: trackColor, scaleColor: scaleColor, barColor: barColor, lineWidth: lineWidth, lineCap: lineCap, size: size }); } /* Main Pie Chart */ if ($('.main-pie')[0]) { easyPieChart('main-pie', 'rgba(255,255,255,0.2)', 'rgba(255,255,255,0.5)', 'rgba(255,255,255,0.7)', 7, 'butt', 148); } /* Others */ if ($('.sub-pie-1')[0]) { easyPieChart('sub-pie-1', '#eee', '#ccc', '#2196F3', 4, 'butt', 95); } if ($('.sub-pie-2')[0]) { easyPieChart('sub-pie-2', '#eee', '#ccc', '#FFC107', 4, 'butt', 95); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/common/Feng.js ================================================ var Feng = { ctxPath: "", addCtx: function (ctx) { if (this.ctxPath == "") { this.ctxPath = ctx; } }, notify:function(message,type){ $.growl({ message: message },{ type: type, allow_dismiss: false, label: 'Cancel', className: 'btn-xs btn-inverse', placement: { from: 'top', align: 'center' }, delay: 3500, animate: { enter: 'animated fadeIn', exit: 'animated fadeOut' }, offset: { x: 20, y: 85 } }); }, info: function (info) { Feng.notify(info, 'info'); }, success: function (info) { Feng.notify(info, 'success'); }, warning: function (info) { Feng.notify(info, 'warning'); }, error: function (info) { Feng.notify(info, 'danger'); }, /** * 确认框 * @param tip 确认提示 * @param ensure 确认操作 * @param subTip 更详细的确认询问信息,可以不传改参数 */ confirm: function (tip, ensure,subTip) { swal({ title: tip, text: subTip?subTip:"", type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "确定", cancelButtonText:'取消', closeOnConfirm: true }, function(){ // swal("Deleted!", "Your imaginary file has been deleted.", "success"); ensure(); }); }, log: function (info) { console.log(info); }, alert: function (info, iconIndex) { parent.layer.msg(info, { icon: iconIndex }); }, infoDetail: function (title, info) { var display = ""; if (typeof info == "string") { display = info; } else { if (info instanceof Array) { for (var x in info) { display = display + info[x] + "
          "; } } else { display = info; } } parent.layer.open({ title: title, type: 1, skin: 'layui-layer-rim', //加上边框 area: ['950px', '600px'], //宽高 content: '
          ' + display + '
          ' }); }, writeObj: function (obj) { var description = ""; for (var i in obj) { var property = obj[i]; description += i + " = " + property + ","; } layer.alert(description, { skin: 'layui-layer-molv', closeBtn: 0 }); }, showInputTree: function (inputId, inputTreeContentId, leftOffset, rightOffset) { var onBodyDown = function (event) { if (!(event.target.id == "menuBtn" || event.target.id == inputTreeContentId || $(event.target).parents("#" + inputTreeContentId).length > 0)) { $("#" + inputTreeContentId).fadeOut("fast"); $("body").unbind("mousedown", onBodyDown);// mousedown当鼠标按下就可以触发,不用弹起 } }; if(leftOffset == undefined && rightOffset == undefined){ var inputDiv = $("#" + inputId); var inputDivOffset = $("#" + inputId).offset(); $("#" + inputTreeContentId).css({ left: inputDivOffset.left + "px", top: inputDivOffset.top + inputDiv.outerHeight() + "px" }).slideDown("fast"); }else{ $("#" + inputTreeContentId).css({ left: leftOffset + "px", top: rightOffset + "px" }).slideDown("fast"); } $("body").bind("mousedown", onBodyDown); }, baseAjax: function (url, tip) { var ajax = new $ax(Feng.ctxPath + url, function (data) { Feng.success(tip + "成功!"); }, function (data) { Feng.error(tip + "失败!" + data.responseJSON.message + "!"); }); return ajax; }, changeAjax: function (url) { return Feng.baseAjax(url, "修改"); }, zTreeCheckedNodes: function (zTreeId) { var zTree = $.fn.zTree.getZTreeObj(zTreeId); var nodes = zTree.getCheckedNodes(); var ids = ""; for (var i = 0, l = nodes.length; i < l; i++) { ids += "," + nodes[i].id; } return ids.substring(1); }, eventParseObject: function (event) {//获取点击事件的源对象 event = event ? event : window.event; var obj = event.srcElement ? event.srcElement : event.target; return $(obj); }, sessionTimeoutRegistry: function () { $.ajaxSetup({ contentType: "application/x-www-form-urlencoded;charset=utf-8", complete: function (XMLHttpRequest, textStatus) { //通过XMLHttpRequest取得响应头,sessionstatus, var sessionstatus = XMLHttpRequest.getResponseHeader("sessionstatus"); if (sessionstatus == "timeout") { //如果超时就处理 ,指定要跳转的页面 window.location = Feng.ctxPath + "/global/sessionError"; } } }); }, initValidator: function(formId,fields){ $('#' + formId).bootstrapValidator({ feedbackIcons: { valid: 'glyphicon glyphicon-ok', invalid: 'glyphicon glyphicon-remove', validating: 'glyphicon glyphicon-refresh' }, fields: fields, live: 'enabled', message: '该字段不能为空' }); }, underLineToCamel: function (str) { var strArr = str.split('_'); for (var i = 1; i < strArr.length; i++) { strArr[i] = strArr[i].charAt(0).toUpperCase() + strArr[i].substring(1); } var result = strArr.join(''); return result.charAt(0).toUpperCase() + result.substring(1); } }; ================================================ FILE: material-manage/src/main/webapp/static/js/common/ajax-object.js ================================================ (function () { var $ax = function (url, success, error) { this.url = url; this.type = "post"; this.data = {}; this.dataType = "json"; this.async = false; this.success = success; this.error = error; }; $ax.prototype = { start : function () { var me = this; if (this.url.indexOf("?") == -1) { this.url = this.url + "?jstime=" + new Date().getTime(); } else { this.url = this.url + "&jstime=" + new Date().getTime(); } $.ajax({ type: this.type, url: this.url, dataType: this.dataType, async: this.async, data: this.data, beforeSend: function(data) { }, success: function(data) { if(data.code && data.code != 200){ Feng.error(data.message); }else { me.success(data); } }, error: function(data) { //如果后台通过spring validator返回错误信息,则取第一条错误信息并显示 if(data.responseJSON.errors && data.responseJSON.errors.length>0){ data.responseJSON.message = data.responseJSON.errors[0].defaultMessage; } me.error(data); } }); }, set : function (key, value) { if (typeof key == "object") { for (var i in key) { if (typeof i == "function") continue; this.data[i] = key[i]; } } else { this.data[key] = (typeof value == "undefined") ? $("#" + key).val() : value; } return this; }, setData : function(data){ this.data = data; return this; }, clear : function () { this.data = {}; return this; }, setType: function(type){ this.type = type; } }; window.$ax = $ax; } ()); ================================================ FILE: material-manage/src/main/webapp/static/js/common/bootstrap-table-object.js ================================================ /** * 初始化 BootStrap Table 的封装 * * 约定:toolbar的id为 (bstableId + "Toolbar") * * @author fengshuonan */ (function () { var BSTable = function (bstableId, url, columns) { this.btInstance = null; //jquery和BootStrapTable绑定的对象 this.bstableId = bstableId; this.url = Feng.ctxPath + url; this.method = "post"; this.paginationType = "server"; //默认分页方式是服务器分页,可选项"client" this.toolbarId = bstableId + "Toolbar"; this.columns = columns; this.height = 665; //默认表格高度665 this.data = {}; this.queryParams = {}; // 向后台传递的自定义参数 }; BSTable.prototype = { /** * 初始化bootstrap table */ init: function () { var tableId = this.bstableId; var me = this; this.btInstance = $('#' + tableId).bootstrapTable({ contentType: "application/x-www-form-urlencoded", url: this.url, //请求地址 method: this.method, //ajax方式,post还是get ajaxOptions: { //ajax请求的附带参数 data: this.data }, toolbar: "#" + this.toolbarId,//顶部工具条 striped: true, //是否显示行间隔色 cache: false, //是否使用缓存,默认为true pagination: true, //是否显示分页(*) sortable: true, //是否启用排序 sortOrder: "desc", //排序方式 pageNumber: 1, //初始化加载第一页,默认第一页 pageSize: 14, //每页的记录行数(*) pageList: [14, 50, 100], //可供选择的每页的行数(*) queryParamsType: 'limit', //默认值为 'limit' ,在默认情况下 传给服务端的参数为:offset,limit,sort queryParams: function (param) { return $.extend(me.queryParams, param); }, // 向后台传递的自定义参数 sidePagination: this.paginationType, //分页方式:client客户端分页,server服务端分页(*) search: false, //是否显示表格搜索,此搜索是客户端搜索,不会进服务端 strictSearch: true, //设置为 true启用 全匹配搜索,否则为模糊搜索 // showColumns: true, //是否显示所有的列 showRefresh: true, //是否显示刷新按钮 // minimumCountColumns: 2, //最少允许的列数 clickToSelect: false, //是否启用点击选中行 searchOnEnterKey: true, //设置为 true时,按回车触发搜索方法,否则自动触发搜索方法 columns: this.columns, //列数组 pagination: true, //是否显示分页条 height: this.height, icons: { refresh: 'glyphicon-repeat', toggle: 'glyphicon-list-alt', columns: 'glyphicon-list' }, iconSize: 'outline' }); return this; }, /** * 向后台传递的自定义参数 * @param param */ setQueryParams: function (param) { this.queryParams = param; }, /** * 设置分页方式:server 或者 client */ setPaginationType: function (type) { this.paginationType = type; }, /** * 设置ajax post请求时候附带的参数 */ set: function (key, value) { if (typeof key == "object") { for (var i in key) { if (typeof i == "function") continue; this.data[i] = key[i]; } } else { this.data[key] = (typeof value == "undefined") ? $("#" + key).val() : value; } return this; }, /** * 设置ajax post请求时候附带的参数 */ setData: function (data) { this.data = data; return this; }, /** * 清空ajax post请求参数 */ clear: function () { this.data = {}; return this; }, /** * 刷新 bootstrap 表格 * Refresh the remote server data, * you can set {silent: true} to refresh the data silently, * and set {url: newUrl} to change the url. * To supply query params specific to this request, set {query: {foo: 'bar'}} */ refresh: function (parms) { if (typeof parms != "undefined") { this.btInstance.bootstrapTable('refresh', parms); } else { this.btInstance.bootstrapTable('refresh'); } } }; window.BSTable = BSTable; }()); ================================================ FILE: material-manage/src/main/webapp/static/js/common/tree-table-object.js ================================================ /** * 初始化 Tree Table 的封装 * * @author cyf */ (function () { var BSTreeTable = function (bstableId, url, columns) { this.btInstance = null; //jquery和bootstrapTreeTable绑定的对象 this.bstableId = bstableId; this.url = Feng.ctxPath + url; this.method = "post"; this.columns = columns; this.data = {};// ajax的参数 this.expandColumn = null;// 展开显示的列 this.id = 'id';// 选取记录返回的值 this.code = 'code';// 用于设置父子关系 this.parentCode = 'pcode';// 用于设置父子关系 this.expandAll = false;// 是否默认全部展开 this.toolbarId = bstableId + "Toolbar"; this.height = 665; //默认表格高度665 }; BSTreeTable.prototype = { /** * 初始化bootstrap table */ init: function () { var tableId = this.bstableId; this.btInstance = $('#'+tableId).bootstrapTreeTable({ id: this.id,// 选取记录返回的值 code: this.code,// 用于设置父子关系 parentCode: this.parentCode,// 用于设置父子关系 rootCodeValue: this.rootCodeValue,//设置根节点code值----可指定根节点,默认为null,"",0,"0" type: this.method, //请求数据的ajax类型 url: this.url, //请求数据的ajax的url ajaxParams: this.data, //请求数据的ajax的data属性 expandColumn: this.expandColumn,//在哪一列上面显示展开按钮,从0开始 striped: true, //是否各行渐变色 expandAll: this.expandAll, //是否全部展开 columns: this.columns, //列数组 toolbar: "#" + this.toolbarId,//顶部工具条 height: this.height, }); return this; }, /** * 设置在哪一列上面显示展开按钮,从0开始 */ setExpandColumn: function (expandColumn) { this.expandColumn = expandColumn; }, /** * 设置记录返回的id值 */ setIdField: function (id) { this.id = id; }, /** * 设置记录分级的字段 */ setCodeField: function (code) { this.code = code; }, /** * 设置记录分级的父级字段 */ setParentCodeField: function (parentCode) { this.parentCode = parentCode; }, /** * 设置根节点code值----可指定根节点,默认为null,"",0,"0" */ setRootCodeValue: function (rootCodeValue) { this.rootCodeValue = rootCodeValue; }, /** * 设置是否默认全部展开 */ setExpandAll: function (expandAll) { this.expandAll = expandAll; }, /** * 设置表格高度 */ setHeight: function (height) { this.height = height; }, /** * 设置ajax post请求时候附带的参数 */ set: function (key, value) { if (typeof key == "object") { for (var i in key) { if (typeof i == "function") continue; this.data[i] = key[i]; } } else { this.data[key] = (typeof value == "undefined") ? $("#" + key).val() : value; } return this; }, /** * 设置ajax post请求时候附带的参数 */ setData: function (data) { this.data = data; return this; }, /** * 清空ajax post请求参数 */ clear: function () { this.data = {}; return this; }, /** * 刷新表格 */ refresh: function (parms) { if (typeof parms != "undefined") { this.btInstance.bootstrapTreeTable('refresh', parms.query);// 为了兼容bootstrap-table的写法 } else { this.btInstance.bootstrapTreeTable('refresh'); } } }; window.BSTreeTable = BSTreeTable; }()); ================================================ FILE: material-manage/src/main/webapp/static/js/common/web-upload-object.js ================================================ /** * web-upload 工具类 * * 约定: * 上传按钮的id = 图片隐藏域id + 'BtnId' * 图片预览框的id = 图片隐藏域id + 'PreId' * * @author fengshuonan */ (function() { var $WebUpload = function(pictureId) { this.pictureId = pictureId; this.uploadBtnId = pictureId + "BtnId"; this.uploadPreId = pictureId + "PreId"; this.uploadUrl = Feng.ctxPath + '/mgr/upload'; this.fileSizeLimit = 100 * 1024 * 1024; this.picWidth = 800; this.picHeight = 800; this.uploadBarId = null; }; $WebUpload.prototype = { /** * 初始化webUploader */ init : function() { var uploader = this.create(); this.bindEvent(uploader); return uploader; }, /** * 创建webuploader对象 */ create : function() { var webUploader = WebUploader.create({ auto : true, pick : { id : '#' + this.uploadBtnId, multiple : false,// 只上传一个 }, accept : { title : 'Images', extensions : 'gif,jpg,jpeg,bmp,png', mimeTypes : 'image/gif,image/jpg,image/jpeg,image/bmp,image/png' }, swf : Feng.ctxPath + '/static/js/plugins/webuploader/Uploader.swf', disableGlobalDnd : true, duplicate : true, server : this.uploadUrl, fileSingleSizeLimit : this.fileSizeLimit }); return webUploader; }, /** * 绑定事件 */ bindEvent : function(bindedObj) { var me = this; bindedObj.on('fileQueued', function(file) { var $li = $('
          '); var $img = $li.find('img'); $("#" + me.uploadPreId).html($li); // 生成缩略图 bindedObj.makeThumb(file, function(error, src) { if (error) { $img.replaceWith('不能预览'); return; } $img.attr('src', src); }, me.picWidth, me.picHeight); }); // 文件上传过程中创建进度条实时显示。 bindedObj.on('uploadProgress', function(file, percentage) { $("#"+me.uploadBarId).css("width",percentage * 100 + "%"); }); // 文件上传成功,给item添加成功class, 用样式标记上传成功。 bindedObj.on('uploadSuccess', function(file,response) { Feng.success("上传成功"); $("#" + me.pictureId).val(response); }); // 文件上传失败,显示上传出错。 bindedObj.on('uploadError', function(file) { Feng.error("上传失败"); }); // 其他错误 bindedObj.on('error', function(type) { if ("Q_EXCEED_SIZE_LIMIT" == type) { Feng.error("文件大小超出了限制"); } else if ("Q_TYPE_DENIED" == type) { Feng.error("文件类型不满足"); } else if ("Q_EXCEED_NUM_LIMIT" == type) { Feng.error("上传数量超过限制"); } else if ("F_DUPLICATE" == type) { Feng.error("图片选择重复"); } else { Feng.error("上传过程中出错"); } }); // 完成上传完了,成功或者失败 bindedObj.on('uploadComplete', function(file) { }); }, /** * 设置图片上传的进度条的id */ setUploadBarId: function (id) { this.uploadBarId = id; } }; window.$WebUpload = $WebUpload; }()); ================================================ FILE: material-manage/src/main/webapp/static/js/common/ztree-object.js ================================================ /** * ztree插件的封装 */ (function() { var $ZTree = function(id, url) { this.id = id; this.url = url; this.onClick = null; this.settings = null; this.ondblclick=null; }; $ZTree.prototype = { /** * 初始化ztree的设置 */ initSetting : function() { var settings = { view : { dblClickExpand : true, selectedMulti : false, showIcon:false }, data : {simpleData : {enable : true}}, callback : { onClick : this.onClick, onDblClick:this.ondblclick } }; return settings; }, /** * 手动设置ztree的设置 */ setSettings : function(val) { this.settings = val; }, /** * 初始化ztree */ init : function() { var zNodeSeting = null; if(this.settings != null){ zNodeSeting = this.settings; }else{ zNodeSeting = this.initSetting(); } var zNodes = this.loadNodes(); $.fn.zTree.init($("#" + this.id), zNodeSeting, zNodes); }, /** * 绑定onclick事件 */ bindOnClick : function(func) { this.onClick = func; }, /** * 绑定双击事件 */ bindOnDblClick : function(func) { this.ondblclick=func; }, /** * 加载节点 */ loadNodes : function() { var zNodes = null; var ajax = new $ax(Feng.ctxPath + this.url, function(data) { zNodes = data; }, function(data) { Feng.error("加载ztree信息失败!"); }); ajax.start(); return zNodes; }, /** * 获取选中的值 */ getSelectedVal : function(){ var zTree = $.fn.zTree.getZTreeObj(this.id); var nodes = zTree.getSelectedNodes(); return nodes[0].name; } }; window.$ZTree = $ZTree; }()); ================================================ FILE: material-manage/src/main/webapp/static/js/demo.js ================================================ $(window).load(function(){ //Welcome Message (not for login page) function notify(message, type){ $.growl({ message: message },{ type: type, allow_dismiss: false, label: 'Cancel', className: 'btn-xs btn-inverse', placement: { from: 'top', align: 'right' }, delay: 11500, animate: { enter: 'animated fadeIn', exit: 'animated fadeOut' }, offset: { x: 20, y: 85 } }); }; if (!$('.login-content')[0]) { notify('欢迎光临 Flash Material', 'inverse'); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/flot-charts/bar-chart.js ================================================ $(document).ready(function(){ /* Make some random data for Flot Line Chart*/ var data1 = [[1,60], [2,30], [3,50], [4,100], [5,10], [6,90], [7,85]]; var data2 = [[1,20], [2,90], [3,60], [4,40], [5,100], [6,25], [7,65]]; var data3 = [[1,100], [2,20], [3,60], [4,90], [5,80], [6,10], [7,5]]; /* Create an Array push the data + Draw the bars*/ var barData = new Array(); barData.push({ data : data1, label: 'Tokyo', bars : { show : true, barWidth : 0.08, order : 1, lineWidth: 0, fillColor: '#8BC34A' } }); barData.push({ data : data2, label: 'Seoul', bars : { show : true, barWidth : 0.08, order : 2, lineWidth: 0, fillColor: '#00BCD4' } }); barData.push({ data : data3, label: 'Beijing', bars : { show : true, barWidth : 0.08, order : 3, lineWidth: 0, fillColor: '#FF9800' } }); /* Let's create the chart */ if ($('#bar-chart')[0]) { $.plot($("#bar-chart"), barData, { grid : { borderWidth: 1, borderColor: '#eee', show : true, hoverable : true, clickable : true }, yaxis: { tickColor: '#eee', tickDecimals: 0, font :{ lineHeight: 13, style: "normal", color: "#9f9f9f", }, shadowSize: 0 }, xaxis: { tickColor: '#fff', tickDecimals: 0, font :{ lineHeight: 13, style: "normal", color: "#9f9f9f" }, shadowSize: 0, }, legend:{ container: '.flc-bar', backgroundOpacity: 0.5, noColumns: 0, backgroundColor: "white", lineWidth: 0 } }); } /* Tooltips for Flot Charts */ if ($(".flot-chart")[0]) { $(".flot-chart").bind("plothover", function (event, pos, item) { if (item) { var x = item.datapoint[0].toFixed(2), y = item.datapoint[1].toFixed(2); $(".flot-tooltip").html(item.series.label + " of " + x + " = " + y).css({top: item.pageY+5, left: item.pageX+5}).show(); } else { $(".flot-tooltip").hide(); } }); $("
          ").appendTo("body"); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/flot-charts/curved-line-chart.js ================================================ $(document).ready(function(){ /* Make some random data for the Chart*/ var d1 = []; for (var i = 0; i <= 10; i += 1) { d1.push([i, parseInt(Math.random() * 30)]); } var d2 = []; for (var i = 0; i <= 20; i += 1) { d2.push([i, parseInt(Math.random() * 30)]); } var d3 = []; for (var i = 0; i <= 10; i += 1) { d3.push([i, parseInt(Math.random() * 30)]); } /* Chart Options */ var options = { series: { shadowSize: 0, curvedLines: { //This is a third party plugin to make curved lines apply: true, active: true, monotonicFit: true }, lines: { show: false, lineWidth: 0, }, }, grid: { borderWidth: 0, labelMargin:10, hoverable: true, clickable: true, mouseActiveRadius:6, }, xaxis: { tickDecimals: 0, ticks: false }, yaxis: { tickDecimals: 0, ticks: false }, legend: { show: false } }; /* Let's create the chart */ if ($("#curved-line-chart")[0]) { $.plot($("#curved-line-chart"), [ {data: d1, lines: { show: true, fill: 0.98 }, label: 'Product 1', stack: true, color: '#e3e3e3' }, {data: d3, lines: { show: true, fill: 0.98 }, label: 'Product 2', stack: true, color: '#f1dd2c' } ], options); } /* Tooltips for Flot Charts */ if ($(".flot-chart")[0]) { $(".flot-chart").bind("plothover", function (event, pos, item) { if (item) { var x = item.datapoint[0].toFixed(2), y = item.datapoint[1].toFixed(2); $(".flot-tooltip").html(item.series.label + " of " + x + " = " + y).css({top: item.pageY+5, left: item.pageX+5}).show(); } else { $(".flot-tooltip").hide(); } }); $("
          ").appendTo("body"); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/flot-charts/dynamic-chart.js ================================================ $(document).ready(function(){ /* Make some random data*/ var data = []; var totalPoints = 300; var updateInterval = 30; function getRandomData() { if (data.length > 0) data = data.slice(1); while (data.length < totalPoints) { var prev = data.length > 0 ? data[data.length - 1] : 50, y = prev + Math.random() * 10 - 5; if (y < 0) { y = 0; } else if (y > 90) { y = 90; } data.push(y); } var res = []; for (var i = 0; i < data.length; ++i) { res.push([i, data[i]]) } return res; } /* Create Chart */ if ($('#dynamic-chart')[0]) { var plot = $.plot("#dynamic-chart", [ getRandomData() ], { series: { label: "Server Process Data", lines: { show: true, lineWidth: 0.2, fill: 0.6 }, color: '#00BCD4', shadowSize: 0, }, yaxis: { min: 0, max: 100, tickColor: '#eee', font :{ lineHeight: 13, style: "normal", color: "#9f9f9f", }, shadowSize: 0, }, xaxis: { tickColor: '#eee', show: true, font :{ lineHeight: 13, style: "normal", color: "#9f9f9f", }, shadowSize: 0, min: 0, max: 250 }, grid: { borderWidth: 1, borderColor: '#eee', labelMargin:10, hoverable: true, clickable: true, mouseActiveRadius:6, }, legend:{ container: '.flc-dynamic', backgroundOpacity: 0.5, noColumns: 0, backgroundColor: "white", lineWidth: 0 } }); } /* Update */ function update() { plot.setData([getRandomData()]); // Since the axes don't change, we don't need to call plot.setupGrid() plot.draw(); setTimeout(update, updateInterval); } update(); }); ================================================ FILE: material-manage/src/main/webapp/static/js/flot-charts/line-chart.js ================================================ $(document).ready(function(){ /* Make some random data for Recent Items chart */ var data = []; var totalPoints = 100; var updateInterval = 30; function getRandomData() { if (data.length > 0) data = data.slice(1); while (data.length < totalPoints) { var prev = data.length > 0 ? data[data.length - 1] : 50, y = prev + Math.random() * 10 - 5; if (y < 0) { y = 0; } else if (y > 90) { y = 90; } data.push(y); } var res = []; for (var i = 0; i < data.length; ++i) { res.push([i, data[i]]) } return res; } /* Make some random data for Flot Line Chart */ var d1 = []; for (var i = 0; i <= 10; i += 1) { d1.push([i, parseInt(Math.random() * 30)]); } var d2 = []; for (var i = 0; i <= 20; i += 1) { d2.push([i, parseInt(Math.random() * 30)]); } var d3 = []; for (var i = 0; i <= 10; i += 1) { d3.push([i, parseInt(Math.random() * 30)]); } /* Chart Options */ var options = { series: { shadowSize: 0, lines: { show: false, lineWidth: 0, }, }, grid: { borderWidth: 0, labelMargin:10, hoverable: true, clickable: true, mouseActiveRadius:6, }, xaxis: { tickDecimals: 0, ticks: false }, yaxis: { tickDecimals: 0, ticks: false }, legend: { show: false } }; /* Regular Line Chart */ if ($("#line-chart")[0]) { $.plot($("#line-chart"), [ {data: d1, lines: { show: true, fill: 0.98 }, label: 'Product 1', stack: true, color: '#e3e3e3' }, {data: d3, lines: { show: true, fill: 0.98 }, label: 'Product 2', stack: true, color: '#FFC107' } ], options); } /* Recent Items Table Chart */ if ($("#recent-items-chart")[0]) { $.plot($("#recent-items-chart"), [ {data: getRandomData(), lines: { show: true, fill: 0.8 }, label: 'Items', stack: true, color: '#00BCD4' }, ], options); } /* Tooltips for Flot Charts */ if ($(".flot-chart")[0]) { $(".flot-chart").bind("plothover", function (event, pos, item) { if (item) { var x = item.datapoint[0].toFixed(2), y = item.datapoint[1].toFixed(2); $(".flot-tooltip").html(item.series.label + " of " + x + " = " + y).css({top: item.pageY+5, left: item.pageX+5}).show(); } else { $(".flot-tooltip").hide(); } }); $("
          ").appendTo("body"); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/flot-charts/pie-chart.js ================================================ $(document).ready(function(){ var pieData = [ {data: 1, color: '#F44336', label: 'Toyota'}, {data: 2, color: '#03A9F4', label: 'Nissan'}, {data: 3, color: '#8BC34A', label: 'Hyundai'}, {data: 4, color: '#FFEB3B', label: 'Scion'}, {data: 4, color: '#009688', label: 'Daihatsu'}, ]; /* Pie Chart */ if($('#pie-chart')[0]){ $.plot('#pie-chart', pieData, { series: { pie: { show: true, stroke: { width: 2, }, }, }, legend: { container: '.flc-pie', backgroundOpacity: 0.5, noColumns: 0, backgroundColor: "white", lineWidth: 0 }, grid: { hoverable: true, clickable: true }, tooltip: true, tooltipOpts: { content: "%p.0%, %s", // show percentages, rounding to 2 decimal places shifts: { x: 20, y: 0 }, defaultTheme: false, cssClass: 'flot-tooltip' } }); } /* Donut Chart */ if($('#donut-chart')[0]){ $.plot('#donut-chart', pieData, { series: { pie: { innerRadius: 0.5, show: true, stroke: { width: 2, }, }, }, legend: { container: '.flc-donut', backgroundOpacity: 0.5, noColumns: 0, backgroundColor: "white", lineWidth: 0 }, grid: { hoverable: true, clickable: true }, tooltip: true, tooltipOpts: { content: "%p.0%, %s", // show percentages, rounding to 2 decimal places shifts: { x: 20, y: 0 }, defaultTheme: false, cssClass: 'flot-tooltip' } }); } }); ================================================ FILE: material-manage/src/main/webapp/static/js/functions.js ================================================ /* * Detact Mobile Browser */ if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { $('html').addClass('ismobile'); } $(document).ready(function(){ /* -------------------------------------------------------- Layout -----------------------------------------------------------*/ (function () { //Get saved layout type from LocalStorage var layoutStatus = localStorage.getItem('ma-layout-status'); if(!$('#header-2')[0]) { //Make it work only on normal headers if (layoutStatus == 1) { $('body').addClass('sw-toggled'); $('#tw-switch').prop('checked', true); } } $('body').on('change', '#toggle-width input:checkbox', function () { if ($(this).is(':checked')) { setTimeout(function () { $('body').addClass('toggled sw-toggled'); localStorage.setItem('ma-layout-status', 1); }, 250); } else { setTimeout(function () { $('body').removeClass('toggled sw-toggled'); localStorage.setItem('ma-layout-status', 0); }, 250); } }); })(); /* -------------------------------------------------------- Scrollbar -----------------------------------------------------------*/ function scrollBar(selector, theme, mousewheelaxis) { $(selector).mCustomScrollbar({ theme: theme, scrollInertia: 100, axis:'yx', mouseWheel: { enable: true, axis: mousewheelaxis, preventDefault: true } }); } if (!$('html').hasClass('ismobile')) { //On Custom Class if ($('.c-overflow')[0]) { scrollBar('.c-overflow', 'minimal-dark', 'y'); } } /* * Top Search */ (function(){ $('body').on('click', '#top-search > a', function(e){ e.preventDefault(); $('#header').addClass('search-toggled'); $('#top-search-wrap input').focus(); }); $('body').on('click', '#top-search-close', function(e){ e.preventDefault(); $('#header').removeClass('search-toggled'); }); })(); /* * Sidebar */ (function(){ //Toggle $('body').on('click', '#menu-trigger, #chat-trigger', function(e){ e.preventDefault(); var x = $(this).data('trigger'); $(x).toggleClass('toggled'); $(this).toggleClass('open'); //Close opened sub-menus $('.sub-menu.toggled').not('.active').each(function(){ $(this).removeClass('toggled'); $(this).find('ul').hide(); }); $('.profile-menu .main-menu').hide(); if (x == '#sidebar') { $elem = '#sidebar'; $elem2 = '#menu-trigger'; $('#chat-trigger').removeClass('open'); if (!$('#chat').hasClass('toggled')) { $('#header').toggleClass('sidebar-toggled'); } else { $('#chat').removeClass('toggled'); } } if (x == '#chat') { $elem = '#chat'; $elem2 = '#chat-trigger'; $('#menu-trigger').removeClass('open'); if (!$('#sidebar').hasClass('toggled')) { $('#header').toggleClass('sidebar-toggled'); } else { $('#sidebar').removeClass('toggled'); } } //When clicking outside if ($('#header').hasClass('sidebar-toggled')) { $(document).on('click', function (e) { if (($(e.target).closest($elem).length === 0) && ($(e.target).closest($elem2).length === 0)) { setTimeout(function(){ $($elem).removeClass('toggled'); $('#header').removeClass('sidebar-toggled'); $($elem2).removeClass('open'); }); } }); } }) //Submenu $('body').on('click', '.sub-menu > a', function(e){ e.preventDefault(); $(this).next().slideToggle(200); $(this).parent().toggleClass('toggled'); }); })(); /* * Clear Notification */ $('body').on('click', '[data-clear="notification"]', function(e){ e.preventDefault(); var x = $(this).closest('.listview'); var y = x.find('.lv-item'); var z = y.size(); $(this).parent().fadeOut(); x.find('.list-group').prepend(''); x.find('.grid-loading').fadeIn(1500); var w = 0; y.each(function(){ var z = $(this); setTimeout(function(){ z.addClass('animated fadeOutRightBig').delay(1000).queue(function(){ z.remove(); }); }, w+=150); }) //Popup empty message setTimeout(function(){ $('#notifications').addClass('empty'); }, (z*150)+200); }); /* * Dropdown Menu */ if($('.dropdown')[0]) { //Propagate $('body').on('click', '.dropdown.open .dropdown-menu', function(e){ e.stopPropagation(); }); $('.dropdown').on('shown.bs.dropdown', function (e) { if($(this).attr('data-animation')) { $animArray = []; $animation = $(this).data('animation'); $animArray = $animation.split(','); $animationIn = 'animated '+$animArray[0]; $animationOut = 'animated '+ $animArray[1]; $animationDuration = '' if(!$animArray[2]) { $animationDuration = 500; //if duration is not defined, default is set to 500ms } else { $animationDuration = $animArray[2]; } $(this).find('.dropdown-menu').removeClass($animationOut) $(this).find('.dropdown-menu').addClass($animationIn); } }); $('.dropdown').on('hide.bs.dropdown', function (e) { if($(this).attr('data-animation')) { e.preventDefault(); $this = $(this); $dropdownMenu = $this.find('.dropdown-menu'); $dropdownMenu.addClass($animationOut); setTimeout(function(){ $this.removeClass('open') }, $animationDuration); } }); } /* * Todo Add new item */ if ($('#todo-lists')[0]) { //Add Todo Item $('body').on('click', '#add-tl-item .add-new-item', function(){ $(this).parent().addClass('toggled'); }); //Dismiss $('body').on('click', '.add-tl-actions > a', function(e){ e.preventDefault(); var x = $(this).closest('#add-tl-item'); var y = $(this).data('tl-action'); if (y == "dismiss") { x.find('textarea').val(''); x.removeClass('toggled'); } if (y == "save") { x.find('textarea').val(''); x.removeClass('toggled'); } }); } /* * Auto Hight Textarea */ if ($('.auto-size')[0]) { autosize($('.auto-size')); } /* * Profile Menu */ $('body').on('click', '.profile-menu > a', function(e){ e.preventDefault(); $(this).parent().toggleClass('toggled'); $(this).next().slideToggle(200); }); /* * Text Feild */ //Add blue animated border and remove with condition when focus and blur if($('.fg-line')[0]) { $('body').on('focus', '.fg-line .form-control', function(){ $(this).closest('.fg-line').addClass('fg-toggled'); }) $('body').on('blur', '.form-control', function(){ var p = $(this).closest('.form-group, .input-group'); var i = p.find('.form-control').val(); if (p.hasClass('fg-float')) { if (i.length == 0) { $(this).closest('.fg-line').removeClass('fg-toggled'); } } else { $(this).closest('.fg-line').removeClass('fg-toggled'); } }); } //Add blue border for pre-valued fg-flot text feilds if($('.fg-float')[0]) { $('.fg-float .form-control').each(function(){ var i = $(this).val(); if (!i.length == 0) { $(this).closest('.fg-line').addClass('fg-toggled'); } }); } /* * Audio and Video */ if($('audio, video')[0]) { $('video,audio').mediaelementplayer(); } /* * Tag Select */ if($('.chosen')[0]) { $('.chosen').chosen({ width: '100%', allow_single_deselect: true }); } /* * Input Slider */ //Basic if($('.input-slider')[0]) { $('.input-slider').each(function(){ var isStart = $(this).data('is-start'); $(this).noUiSlider({ start: isStart, range: { 'min': 0, 'max': 100, } }); }); } //Range slider if($('.input-slider-range')[0]) { $('.input-slider-range').noUiSlider({ start: [30, 60], range: { 'min': 0, 'max': 100 }, connect: true }); } //Range slider with value if($('.input-slider-values')[0]) { $('.input-slider-values').noUiSlider({ start: [ 45, 80 ], connect: true, direction: 'rtl', behaviour: 'tap-drag', range: { 'min': 0, 'max': 100 } }); $('.input-slider-values').Link('lower').to($('#value-lower')); $('.input-slider-values').Link('upper').to($('#value-upper'), 'html'); } /* * Input Mask */ if ($('input-mask')[0]) { $('.input-mask').mask(); } /* * Color Picker */ if ($('.color-picker')[0]) { $('.color-picker').each(function(){ var colorOutput = $(this).closest('.cp-container').find('.cp-value'); $(this).farbtastic(colorOutput); }); } /* * HTML Editor */ if ($('.html-editor')[0]) { $('.html-editor').summernote({ height: 150 }); } if($('.html-editor-click')[0]) { //Edit $('body').on('click', '.hec-button', function(){ $('.html-editor-click').summernote({ focus: true }); $('.hec-save').show(); }) //Save $('body').on('click', '.hec-save', function(){ $('.html-editor-click').code(); $('.html-editor-click').destroy(); $('.hec-save').hide(); notify('Content Saved Successfully!', 'success'); }); } //Air Mode if($('.html-editor-airmod')[0]) { $('.html-editor-airmod').summernote({ airMode: true }); } /* * Date Time Picker */ //Date Time Picker if ($('.date-time-picker')[0]) { $('.date-time-picker').datetimepicker( { locale : 'zh-cn' } ); } //Time if ($('.time-picker')[0]) { $('.time-picker').datetimepicker({ locale : 'zh-cn', format: 'LT' }); } //Date if ($('.date-picker')[0]) { $('.date-picker').datetimepicker({ locale : 'zh-cn', format: 'YYYY-MM-DD' }); } /* * Form Wizard */ if ($('.form-wizard-basic')[0]) { $('.form-wizard-basic').bootstrapWizard({ tabClass: 'fw-nav', 'nextSelector': '.next', 'previousSelector': '.previous' }); } /* * Bootstrap Growl - Notifications popups */ function notify(message, type){ $.growl({ message: message },{ type: type, allow_dismiss: false, label: 'Cancel', className: 'btn-xs btn-inverse', placement: { from: 'top', align: 'right' }, delay: 2500, animate: { enter: 'animated bounceIn', exit: 'animated bounceOut' }, offset: { x: 20, y: 85 } }); }; /* * Waves Animation */ (function(){ Waves.attach('.btn:not(.btn-icon):not(.btn-float)'); Waves.attach('.btn-icon, .btn-float', ['waves-circle', 'waves-float']); Waves.init(); })(); /* * Lightbox */ if ($('.lightbox')[0]) { $('.lightbox').lightGallery({ enableTouch: true }); } /* * Link prevent */ $('body').on('click', '.a-prevent', function(e){ e.preventDefault(); }); /* * Collaspe Fix */ if ($('.collapse')[0]) { //Add active class for opened items $('.collapse').on('show.bs.collapse', function (e) { $(this).closest('.panel').find('.panel-heading').addClass('active'); }); $('.collapse').on('hide.bs.collapse', function (e) { $(this).closest('.panel').find('.panel-heading').removeClass('active'); }); //Add active class for pre opened items $('.collapse.in').each(function(){ $(this).closest('.panel').find('.panel-heading').addClass('active'); }); } /* * Tooltips */ if ($('[data-toggle="tooltip"]')[0]) { $('[data-toggle="tooltip"]').tooltip(); } /* * Popover */ if ($('[data-toggle="popover"]')[0]) { $('[data-toggle="popover"]').popover(); } /* * Message */ //Actions if ($('.on-select')[0]) { var checkboxes = '.lv-avatar-content input:checkbox'; var actions = $('.on-select').closest('.lv-actions'); $('body').on('click', checkboxes, function() { if ($(checkboxes+':checked')[0]) { actions.addClass('toggled'); } else { actions.removeClass('toggled'); } }); } if($('#ms-menu-trigger')[0]) { $('body').on('click', '#ms-menu-trigger', function(e){ e.preventDefault(); $(this).toggleClass('open'); $('.ms-menu').toggleClass('toggled'); }); } /* * Login */ if ($('.login-content')[0]) { //Add class to HTML. This is used to center align the logn box $('html').addClass('login-content'); $('body').on('click', '.login-navigation > li', function(){ var z = $(this).data('block'); var t = $(this).closest('.lc-block'); t.removeClass('toggled'); setTimeout(function(){ $(z).addClass('toggled'); }); }) } /* * Fullscreen Browsing */ if ($('[data-action="fullscreen"]')[0]) { var fs = $("[data-action='fullscreen']"); fs.on('click', function(e) { e.preventDefault(); //Launch function launchIntoFullscreen(element) { if(element.requestFullscreen) { element.requestFullscreen(); } else if(element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if(element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if(element.msRequestFullscreen) { element.msRequestFullscreen(); } } //Exit function exitFullscreen() { if(document.exitFullscreen) { document.exitFullscreen(); } else if(document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if(document.webkitExitFullscreen) { document.webkitExitFullscreen(); } } launchIntoFullscreen(document.documentElement); fs.closest('.dropdown').removeClass('open'); }); } /* * Clear Local Storage */ if ($('[data-action="clear-localstorage"]')[0]) { var cls = $('[data-action="clear-localstorage"]'); cls.on('click', function(e) { e.preventDefault(); swal({ title: "Are you sure?", text: "All your saved localStorage values will be removed", type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "Yes, delete it!", closeOnConfirm: false }, function(){ localStorage.clear(); swal("Done!", "localStorage is cleared", "success"); }); }); } /* * Profile Edit Toggle */ if ($('[data-pmb-action]')[0]) { $('body').on('click', '[data-pmb-action]', function(e){ e.preventDefault(); var d = $(this).data('pmb-action'); if (d === "edit") { $(this).closest('.pmb-block').toggleClass('toggled'); } if (d === "reset") { $(this).closest('.pmb-block').removeClass('toggled'); } }); } /* * IE 9 Placeholder */ if($('html').hasClass('ie9')) { $('input, textarea').placeholder({ customClass: 'ie9-placeholder' }); } /* * Listview Search */ if ($('.lvh-search-trigger')[0]) { $('body').on('click', '.lvh-search-trigger', function(e){ e.preventDefault(); x = $(this).closest('.lv-header-alt').find('.lvh-search'); x.fadeIn(300); x.find('.lvhs-input').focus(); }); //Close Search $('body').on('click', '.lvh-search-close', function(){ x.fadeOut(300); setTimeout(function(){ x.find('.lvhs-input').val(''); }, 350); }) } /* * Print */ if ($('[data-action="print"]')[0]) { $('body').on('click', '[data-action="print"]', function(e){ e.preventDefault(); window.print(); }) } /* * Typeahead Auto Complete */ if($('.typeahead')[0]) { var statesArray = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming' ]; var states = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace, local: statesArray }); $('.typeahead').typeahead({ hint: true, highlight: true, minLength: 1 }, { name: 'states', source: states }); } /* * Wall */ if ($('.wcc-toggle')[0]) { var z = '
          ' + '' + '
          ' + '
          ' + '' + '' + '
          ' $('body').on('click', '.wcc-toggle', function() { $(this).parent().html(z); autosize($('.auto-size')); //Reload Auto size textarea }); //Cancel $('body').on('click', '.wcc-cencel', function(e) { e.preventDefault(); $(this).closest('.wc-comment').find('.wcc-inner').addClass('wcc-toggle').html('Write Something...') }); } /* * Skin Change */ $('body').on('click', '[data-skin]', function() { var currentSkin = $('[data-current-skin]').data('current-skin'); var skin = $(this).data('skin'); $('[data-current-skin]').attr('data-current-skin', skin) }); }); ================================================ FILE: material-manage/src/main/webapp/static/js/hplus.js ================================================ //自定义js //公共配置 $(document).ready(function () { // MetsiMenu $('#side-menu').metisMenu(); // 打开右侧边栏 $('.right-sidebar-toggle').click(function () { $('#right-sidebar').toggleClass('sidebar-open'); }); // 右侧边栏使用slimscroll $('.sidebar-container').slimScroll({ height: '100%', railOpacity: 0.4, wheelStep: 10 }); // 打开聊天窗口 $('.open-small-chat').click(function () { $(this).children().toggleClass('fa-comments').toggleClass('fa-remove'); $('.small-chat-box').toggleClass('active'); }); // 聊天窗口使用slimscroll $('.small-chat-box .content').slimScroll({ height: '234px', railOpacity: 0.4 }); // Small todo handler $('.check-link').click(function () { var button = $(this).find('i'); var label = $(this).next('span'); button.toggleClass('fa-check-square').toggleClass('fa-square-o'); label.toggleClass('todo-completed'); return false; }); //固定菜单栏 $(function () { $('.sidebar-collapse').slimScroll({ height: '100%', railOpacity: 0.9, alwaysVisible: false }); }); // 菜单切换 $('.navbar-minimalize').click(function () { $("body").toggleClass("mini-navbar"); SmoothlyMenu(); }); // 侧边栏高度 function fix_height() { var heightWithoutNavbar = $("body > #wrapper").height() - 61; $(".sidebard-panel").css("min-height", heightWithoutNavbar + "px"); } fix_height(); $(window).bind("load resize click scroll", function () { if (!$("body").hasClass('body-small')) { fix_height(); } }); //侧边栏滚动 $(window).scroll(function () { if ($(window).scrollTop() > 0 && !$('body').hasClass('fixed-nav')) { $('#right-sidebar').addClass('sidebar-top'); } else { $('#right-sidebar').removeClass('sidebar-top'); } }); $('.full-height-scroll').slimScroll({ height: '100%' }); $('#side-menu>li').click(function () { if ($('body').hasClass('mini-navbar')) { NavToggle(); } }); $('#side-menu>li li a').click(function () { if ($(window).width() < 769) { NavToggle(); } }); //点击菜单的时候高亮显示菜单 $("a[name='tabMenuItem']").click(function(){ clearTabMenuItem(); $(this).addClass("tab-menu-selected"); }); $('.nav-close').click(NavToggle); //ios浏览器兼容性处理 if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { $('#content-main').css('overflow-y', 'auto'); } }); $(window).bind("load resize", function () { if ($(this).width() < 769) { $('body').addClass('mini-navbar'); $('.navbar-static-side').fadeIn(); } }); function clearTabMenuItem(){ $("a[name='tabMenuItem']").each(function(){ $(this).removeClass("tab-menu-selected"); }); } function highLightMenuItem(hrefVal){ clearTabMenuItem(); $("a[href='" + hrefVal + "']").addClass("tab-menu-selected"); } function NavToggle() { $('.navbar-minimalize').trigger('click'); } function SmoothlyMenu() { if (!$('body').hasClass('mini-navbar')) { $('#side-menu').hide(); setTimeout( function () { $('#side-menu').fadeIn(500); }, 100); } else if ($('body').hasClass('fixed-sidebar')) { $('#side-menu').hide(); setTimeout( function () { $('#side-menu').fadeIn(500); }, 300); } else { $('#side-menu').removeAttr('style'); } } //主题设置 $(function () { // 顶部菜单固定 $('#fixednavbar').click(function () { if ($('#fixednavbar').is(':checked')) { $(".navbar-static-top").removeClass('navbar-static-top').addClass('navbar-fixed-top'); $("body").removeClass('boxed-layout'); $("body").addClass('fixed-nav'); $('#boxedlayout').prop('checked', false); if (localStorageSupport) { localStorage.setItem("boxedlayout", 'off'); } if (localStorageSupport) { localStorage.setItem("fixednavbar", 'on'); } } else { $(".navbar-fixed-top").removeClass('navbar-fixed-top').addClass('navbar-static-top'); $("body").removeClass('fixed-nav'); if (localStorageSupport) { localStorage.setItem("fixednavbar", 'off'); } } }); // 收起左侧菜单 $('#collapsemenu').click(function () { if ($('#collapsemenu').is(':checked')) { $("body").addClass('mini-navbar'); SmoothlyMenu(); if (localStorageSupport) { localStorage.setItem("collapse_menu", 'on'); } } else { $("body").removeClass('mini-navbar'); SmoothlyMenu(); if (localStorageSupport) { localStorage.setItem("collapse_menu", 'off'); } } }); // 固定宽度 $('#boxedlayout').click(function () { if ($('#boxedlayout').is(':checked')) { $("body").addClass('boxed-layout'); $('#fixednavbar').prop('checked', false); $(".navbar-fixed-top").removeClass('navbar-fixed-top').addClass('navbar-static-top'); $("body").removeClass('fixed-nav'); if (localStorageSupport) { localStorage.setItem("fixednavbar", 'off'); } if (localStorageSupport) { localStorage.setItem("boxedlayout", 'on'); } } else { $("body").removeClass('boxed-layout'); if (localStorageSupport) { localStorage.setItem("boxedlayout", 'off'); } } }); // 默认主题 $('.s-skin-0').click(function () { $("body").removeClass("skin-1"); $("body").removeClass("skin-2"); $("body").removeClass("skin-3"); return false; }); // 蓝色主题 $('.s-skin-1').click(function () { $("body").removeClass("skin-2"); $("body").removeClass("skin-3"); $("body").addClass("skin-1"); return false; }); // 黄色主题 $('.s-skin-3').click(function () { $("body").removeClass("skin-1"); $("body").removeClass("skin-2"); $("body").addClass("skin-3"); return false; }); if (localStorageSupport) { var collapse = localStorage.getItem("collapse_menu"); var fixednavbar = localStorage.getItem("fixednavbar"); var boxedlayout = localStorage.getItem("boxedlayout"); if (collapse == 'on') { $('#collapsemenu').prop('checked', 'checked') } if (fixednavbar == 'on') { $('#fixednavbar').prop('checked', 'checked') } if (boxedlayout == 'on') { $('#boxedlayout').prop('checked', 'checked') } } if (localStorageSupport) { var collapse = localStorage.getItem("collapse_menu"); var fixednavbar = localStorage.getItem("fixednavbar"); var boxedlayout = localStorage.getItem("boxedlayout"); var body = $('body'); if (collapse == 'on') { if (!body.hasClass('body-small')) { body.addClass('mini-navbar'); } } if (fixednavbar == 'on') { $(".navbar-static-top").removeClass('navbar-static-top').addClass('navbar-fixed-top'); body.addClass('fixed-nav'); } if (boxedlayout == 'on') { body.addClass('boxed-layout'); } } }); //判断浏览器是否支持html5本地存储 function localStorageSupport() { return (('localStorage' in window) && window['localStorage'] !== null) } ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.js ================================================ /** * Bootstrap Table Chinese translation * Author: Zhixin Wen */ (function ($) { 'use strict'; $.fn.bootstrapTable.locales['zh-CN'] = { formatLoadingMessage: function () { return '正在努力地加载数据中,请稍候……'; }, formatRecordsPerPage: function (pageNumber) { return '每页显示 ' + pageNumber + ' 条记录'; }, formatShowingRows: function (pageFrom, pageTo, totalRows) { return '显示第 ' + pageFrom + ' 到第 ' + pageTo + ' 条记录,总共 ' + totalRows + ' 条记录'; }, formatSearch: function () { return '搜索'; }, formatNoMatches: function () { return '没有找到匹配的记录'; }, formatPaginationSwitch: function () { return '隐藏/显示分页'; }, formatRefresh: function () { return '刷新'; }, formatToggle: function () { return '切换'; }, formatColumns: function () { return '列'; }, formatExport: function () { return '导出数据'; }, formatClearFilters: function () { return '清空过滤'; } }; $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['zh-CN']); })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/dataTables/dataTables.bootstrap.js ================================================ /* Set the defaults for DataTables initialisation */ $.extend(true, $.fn.dataTable.defaults, { "sDom": "<'row'<'col-sm-6'l><'col-sm-6'f>r>" + "t" + "<'row'<'col-sm-6'i><'col-sm-6'p>>", "oLanguage": { "sLengthMenu": "每页 _MENU_ 条记录" } }); /* Default class modification */ $.extend($.fn.dataTableExt.oStdClasses, { "sWrapper": "dataTables_wrapper form-inline", "sFilterInput": "form-control input-sm", "sLengthSelect": "form-control input-sm" }); // In 1.10 we use the pagination renderers to draw the Bootstrap paging, // rather than custom plug-in if ($.fn.dataTable.Api) { $.fn.dataTable.defaults.renderer = 'bootstrap'; $.fn.dataTable.ext.renderer.pageButton.bootstrap = function(settings, host, idx, buttons, page, pages) { var api = new $.fn.dataTable.Api(settings); var classes = settings.oClasses; var lang = settings.oLanguage.oPaginate; var btnDisplay, btnClass; var attach = function(container, buttons) { var i, ien, node, button; var clickHandler = function(e) { e.preventDefault(); if (e.data.action !== 'ellipsis') { api.page(e.data.action).draw(false); } }; for (i = 0, ien = buttons.length; i < ien; i++) { button = buttons[i]; if ($.isArray(button)) { attach(container, button); } else { btnDisplay = ''; btnClass = ''; switch (button) { case 'ellipsis': btnDisplay = '…'; btnClass = 'disabled'; break; case 'first': btnDisplay = lang.sFirst; btnClass = button + (page > 0 ? '' : ' disabled'); break; case 'previous': btnDisplay = lang.sPrevious; btnClass = button + (page > 0 ? '' : ' disabled'); break; case 'next': btnDisplay = lang.sNext; btnClass = button + (page < pages - 1 ? '' : ' disabled'); break; case 'last': btnDisplay = lang.sLast; btnClass = button + (page < pages - 1 ? '' : ' disabled'); break; default: btnDisplay = button + 1; btnClass = page === button ? 'active' : ''; break; } if (btnDisplay) { node = $('
        • ', { 'class': classes.sPageButton + ' ' + btnClass, 'aria-controls': settings.sTableId, 'tabindex': settings.iTabIndex, 'id': idx === 0 && typeof button === 'string' ? settings.sTableId + '_' + button : null }) .append($('', { 'href': '#' }) .html(btnDisplay) ) .appendTo(container); settings.oApi._fnBindAction( node, { action: button }, clickHandler ); } } } }; attach( $(host).empty().html('
            ').children('ul'), buttons ); } } else { // Integration for 1.9- $.fn.dataTable.defaults.sPaginationType = 'bootstrap'; /* API method to get paging information */ $.fn.dataTableExt.oApi.fnPagingInfo = function(oSettings) { return { "iStart": oSettings._iDisplayStart, "iEnd": oSettings.fnDisplayEnd(), "iLength": oSettings._iDisplayLength, "iTotal": oSettings.fnRecordsTotal(), "iFilteredTotal": oSettings.fnRecordsDisplay(), "iPage": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength), "iTotalPages": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength) }; }; /* Bootstrap style pagination control */ $.extend($.fn.dataTableExt.oPagination, { "bootstrap": { "fnInit": function(oSettings, nPaging, fnDraw) { var oLang = oSettings.oLanguage.oPaginate; var fnClickHandler = function(e) { e.preventDefault(); if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) { fnDraw(oSettings); } }; $(nPaging).append( '' ); var els = $('a', nPaging); $(els[0]).bind('click.DT', { action: "previous" }, fnClickHandler); $(els[1]).bind('click.DT', { action: "next" }, fnClickHandler); }, "fnUpdate": function(oSettings, fnDraw) { var iListLength = 5; var oPaging = oSettings.oInstance.fnPagingInfo(); var an = oSettings.aanFeatures.p; var i, ien, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2); if (oPaging.iTotalPages < iListLength) { iStart = 1; iEnd = oPaging.iTotalPages; } else if (oPaging.iPage <= iHalf) { iStart = 1; iEnd = iListLength; } else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) { iStart = oPaging.iTotalPages - iListLength + 1; iEnd = oPaging.iTotalPages; } else { iStart = oPaging.iPage - iHalf + 1; iEnd = iStart + iListLength - 1; } for (i = 0, ien = an.length; i < ien; i++) { // Remove the middle elements $('li:gt(0)', an[i]).filter(':not(:last)').remove(); // Add the new list items and their event handlers for (j = iStart; j <= iEnd; j++) { sClass = (j == oPaging.iPage + 1) ? 'class="active"' : ''; $('
          • ' + j + '
          • ') .insertBefore($('li:last', an[i])[0]) .bind('click', function(e) { e.preventDefault(); oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength; fnDraw(oSettings); }); } // Add / remove disabled classes from the static elements if (oPaging.iPage === 0) { $('li:first', an[i]).addClass('disabled'); } else { $('li:first', an[i]).removeClass('disabled'); } if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) { $('li:last', an[i]).addClass('disabled'); } else { $('li:last', an[i]).removeClass('disabled'); } } } } }); } /* * TableTools Bootstrap compatibility * Required TableTools 2.1+ */ if ($.fn.DataTable.TableTools) { // Set the classes that TableTools uses to something suitable for Bootstrap $.extend(true, $.fn.DataTable.TableTools.classes, { "container": "DTTT btn-group", "buttons": { "normal": "btn btn-default", "disabled": "disabled" }, "collection": { "container": "DTTT_dropdown dropdown-menu", "buttons": { "normal": "", "disabled": "disabled" } }, "print": { "info": "DTTT_print_info modal" }, "select": { "row": "active" } }); // Have the collection use a bootstrap compatible dropdown $.extend(true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { "collection": { "container": "ul", "button": "li", "liner": "a" } }); } ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/dataTables/jquery.dataTables.js ================================================ /*! DataTables 1.10.0-dev * ©2008-2013 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables * @version 1.10.0-dev * @file jquery.dataTables.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2008-2013 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ /*jslint evil: true, undef: true, browser: true */ /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_empty,_intVal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidateRow,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ (/** @lends */function( window, document, $, undefined ) { (function( factory ) { "use strict"; // Define as an AMD module if possible if ( typeof define === 'function' && define.amd ) { define( 'datatables', ['jquery'], factory ); } /* Define using browser globals otherwise * Prevent multiple instantiations if the script is loaded twice */ else if ( jQuery && !jQuery.fn.dataTable ) { factory( jQuery ); } } (/** @lends */function( $ ) { "use strict"; /** * DataTables is a plug-in for the jQuery Javascript library. It is a highly * flexible tool, based upon the foundations of progressive enhancement, * which will add advanced interaction controls to any HTML table. For a * full list of features please refer to * [DataTables.net](href="http://datatables.net). * * Note that the `DataTable` object is not a global variable but is aliased * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may * be accessed. * * @class * @param {object} [init={}] Configuration object for DataTables. Options * are defined by {@link DataTable.defaults} * @requires jQuery 1.3+ * * @example * // Basic initialisation * $(document).ready( function { * $('#example').dataTable(); * } ); * * @example * // Initialisation with configuration options - in this case, disable * // pagination and sorting. * $(document).ready( function { * $('#example').dataTable( { * "paginate": false, * "sort": false * } ); * } ); */ var DataTable; /* * It is useful to have variables which are scoped locally so only the * DataTables functions can access them and they don't leak into global space. * At the same time these functions are often useful over multiple files in the * core and API, so we list, or at least document, all variables which are used * by DataTables as private variables here. This also ensures that there is no * clashing of variable names and that they can easily referenced for reuse. */ // Defined else where // _selector_run // _selector_opts // _selector_first // _selector_row_indexes var _ext; // DataTable.ext var _Api; // DataTable.Api var _api_register; // DataTable.Api.register var _api_registerPlural; // DataTable.Api.registerPlural var _re_new_lines = /[\r\n]/g; var _re_html = /<.*?>/g; var _re_formatted_numeric = /[',$£€¥%]/g; var _re_date_start = /^[\d\+\-a-zA-Z]/; var _empty = function ( d ) { return !d || d === '-' ? true : false; }; var _intVal = function ( s ) { var integer = parseInt( s, 10 ); return !isNaN(integer) && isFinite(s) ? integer : null; }; var _isNumber = function ( d, formatted ) { if ( formatted && typeof d === 'string' ) { d = d.replace( _re_formatted_numeric, '' ); } return !d || d==='-' || (!isNaN( parseFloat(d) ) && isFinite( d )); }; // A string without HTML in it can be considered to be HTML still var _isHtml = function ( d ) { return !d || typeof d === 'string'; }; var _htmlNumeric = function ( d, formatted ) { if ( _empty( d ) ) { return true; } var html = _isHtml( d ); return ! html ? null : _isNumber( _stripHtml( d ), formatted ) ? true : null; }; var _pluck = function ( a, prop, prop2 ) { var out = []; var i=0, ien=a.length; // Could have the test in the loop for slightly smaller code, but speed // is essential here if ( prop2 !== undefined ) { for ( ; i') .css( { position: 'absolute', top: 0, left: 0, height: 1, width: 1, overflow: 'hidden' } ) .append( $('
            ') .css( { position: 'absolute', top: 1, left: 1, width: 100, overflow: 'scroll' } ) .append( $('
            ') .css( { width: '100%', height: 10 } ) ) ) .appendTo( 'body' ); var test = n.find('.test'); // IE6/7 will oversize a width 100% element inside a scrolling element, to // include the width of the scrollbar, while other browsers ensure the inner // element is contained without forcing scrolling browser.bScrollOversize = test[0].offsetWidth === 100; // In rtl text layout, some browsers (most, but not all) will place the // scrollbar on the left, rather than the right. browser.bScrollbarLeft = test.offset().left !== 1; n.remove(); } /** * Add a column to the list used for the table with default values * @param {object} oSettings dataTables settings object * @param {node} nTh The th element for this column * @memberof DataTable#oApi */ function _fnAddColumn( oSettings, nTh ) { var oDefaults = DataTable.defaults.column; var iCol = oSettings.aoColumns.length; var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { "sSortingClass": oSettings.oClasses.sSortable, "sSortingClassJUI": oSettings.oClasses.sSortJUI, "nTh": nTh ? nTh : document.createElement('th'), "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], "mData": oDefaults.mData ? oDefaults.mData : iCol } ); oSettings.aoColumns.push( oCol ); /* Add a column specific filter */ if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) { oSettings.aoPreSearchCols[ iCol ] = $.extend( true, {}, DataTable.models.oSearch ); } else { var oPre = oSettings.aoPreSearchCols[ iCol ]; /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ if ( oPre.bRegex === undefined ) { oPre.bRegex = true; } if ( oPre.bSmart === undefined ) { oPre.bSmart = true; } if ( oPre.bCaseInsensitive === undefined ) { oPre.bCaseInsensitive = true; } } /* Use the column options function to initialise classes etc */ _fnColumnOptions( oSettings, iCol, null ); } /** * Apply options for a column * @param {object} oSettings dataTables settings object * @param {int} iCol column index to consider * @param {object} oOptions object with sType, bVisible and bSearchable etc * @memberof DataTable#oApi */ function _fnColumnOptions( oSettings, iCol, oOptions ) { var oCol = oSettings.aoColumns[ iCol ]; var oClasses = oSettings.oClasses; /* User specified column options */ if ( oOptions !== undefined && oOptions !== null ) { // Backwards compatibility _fnCompatCols( oOptions ); // Map camel case parameters to their Hungarian counterparts _fnCamelToHungarian( DataTable.defaults.column, oOptions ); /* Backwards compatibility for mDataProp */ if ( oOptions.mDataProp !== undefined && !oOptions.mData ) { oOptions.mData = oOptions.mDataProp; } oCol._sManualType = oOptions.sType; // `class` is a reserved word in Javascript, so we need to provide // the ability to use a valid name for the camel case input if ( oOptions.className && ! oOptions.sClass ) { oOptions.sClass = oOptions.className; } $.extend( oCol, oOptions ); _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); /* iDataSort to be applied (backwards compatibility), but aDataSort will take * priority if defined */ if ( typeof oOptions.iDataSort === 'number' ) { oCol.aDataSort = [ oOptions.iDataSort ]; } _fnMap( oCol, oOptions, "aDataSort" ); } /* Cache the data get and set functions for speed */ var mDataSrc = oCol.mData; var mData = _fnGetObjectDataFn( mDataSrc ); var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; var attrTest = function( src ) { return typeof src === 'string' && src.indexOf('@') !== -1; }; oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) ); oCol.fnGetData = function (oData, sSpecific) { var innerData = mData( oData, sSpecific ); if ( oCol.mRender && (sSpecific && sSpecific !== '') ) { return mRender( innerData, sSpecific, oData ); } return innerData; }; oCol.fnSetData = _fnSetObjectDataFn( mDataSrc ); /* Feature sorting overrides column specific when off */ if ( !oSettings.oFeatures.bSort ) { oCol.bSortable = false; } /* Check that the class assignment is correct for sorting */ var bAsc = $.inArray('asc', oCol.asSorting) !== -1; var bDesc = $.inArray('desc', oCol.asSorting) !== -1; if ( !oCol.bSortable || (!bAsc && !bDesc) ) { oCol.sSortingClass = oClasses.sSortableNone; oCol.sSortingClassJUI = ""; } else if ( bAsc && !bDesc ) { oCol.sSortingClass = oClasses.sSortableAsc; oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; } else if ( !bAsc && bDesc ) { oCol.sSortingClass = oClasses.sSortableDesc; oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; } } /** * Adjust the table column widths for new data. Note: you would probably want to * do a redraw after calling this function! * @param {object} settings dataTables settings object * @memberof DataTable#oApi */ function _fnAdjustColumnSizing ( settings ) { /* Not interested in doing column width calculation if auto-width is disabled */ if ( settings.oFeatures.bAutoWidth !== false ) { var columns = settings.aoColumns; _fnCalculateColumnWidths( settings ); for ( var i=0 , iLen=columns.length ; i
            ')[0]; /* Check to see if we should append an id and/or a class name to the container */ cNext = aDom[i+1]; if ( cNext == "'" || cNext == '"' ) { sAttr = ""; j = 2; while ( aDom[i+j] != cNext ) { sAttr += aDom[i+j]; j++; } /* Replace jQuery UI constants @todo depreciated */ if ( sAttr == "H" ) { sAttr = oSettings.oClasses.sJUIHeader; } else if ( sAttr == "F" ) { sAttr = oSettings.oClasses.sJUIFooter; } /* The attribute can be in the format of "#id.class", "#id" or "class" This logic * breaks the string into parts and applies them as needed */ if ( sAttr.indexOf('.') != -1 ) { var aSplit = sAttr.split('.'); nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); nNewNode.className = aSplit[1]; } else if ( sAttr.charAt(0) == "#" ) { nNewNode.id = sAttr.substr(1, sAttr.length-1); } else { nNewNode.className = sAttr; } i += j; /* Move along the position array */ } nInsertNode.appendChild( nNewNode ); nInsertNode = nNewNode; } else if ( cOption == '>' ) { /* End container div */ nInsertNode = nInsertNode.parentNode; } // @todo Move options into their own plugins? else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) { /* Length */ nTmp = _fnFeatureHtmlLength( oSettings ); iPushFeature = 1; } else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) { /* Filter */ nTmp = _fnFeatureHtmlFilter( oSettings ); iPushFeature = 1; } else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) { /* pRocessing */ nTmp = _fnFeatureHtmlProcessing( oSettings ); iPushFeature = 1; } else if ( cOption == 't' ) { /* Table */ nTmp = _fnFeatureHtmlTable( oSettings ); iPushFeature = 1; } else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) { /* Info */ nTmp = _fnFeatureHtmlInfo( oSettings ); iPushFeature = 1; } else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) { /* Pagination */ nTmp = _fnFeatureHtmlPaginate( oSettings ); iPushFeature = 1; } else if ( DataTable.ext.feature.length !== 0 ) { /* Plug-in features */ var aoFeatures = DataTable.ext.feature; for ( var k=0, kLen=aoFeatures.length ; k'; var str = settings.oLanguage.sSearch; str = str.match(/_INPUT_/) ? str.replace('_INPUT_', input) : str+input; var filter = $('
            ', { 'id': ! features.f ? tableId+'_filter' : null, 'class': classes.sFilter } ) .append( $('
          * @param {bool} [redraw=true] redraw the table or not * @returns {array} An array of integers, representing the list of indexes in * aoData ({@link DataTable.models.oSettings}) that have been added to * the table. * @dtopt API * @deprecated Since v1.10 * * @example * // Global var for counter * var giCount = 2; * * $(document).ready(function() { * $('#example').dataTable(); * } ); * * function fnClickAddRow() { * $('#example').dataTable().fnAddData( [ * giCount+".1", * giCount+".2", * giCount+".3", * giCount+".4" ] * ); * * giCount++; * } */ this.fnAddData = function( data, redraw ) { var api = this.api( true ); /* Check if we want to add multiple rows or not */ var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ? api.rows.add( data ) : api.row.add( data ); if ( redraw === undefined || redraw ) { api.draw(); } return rows.flatten().toArray(); }; /** * This function will make DataTables recalculate the column sizes, based on the data * contained in the table and the sizes applied to the columns (in the DOM, CSS or * through the sWidth parameter). This can be useful when the width of the table's * parent element changes (for example a window resize). * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable( { * "sScrollY": "200px", * "bPaginate": false * } ); * * $(window).bind('resize', function () { * oTable.fnAdjustColumnSizing(); * } ); * } ); */ this.fnAdjustColumnSizing = function ( bRedraw ) { var api = this.api( true ).columns.adjust(); var settings = api.settings()[0]; var scroll = settings.oScroll; if ( bRedraw === undefined || bRedraw ) { api.draw( false ); } else if ( scroll.sX !== "" || scroll.sY !== "" ) { /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ _fnScrollDraw( settings ); } }; /** * Quickly and simply clear a table * @param {bool} [bRedraw=true] redraw the table or not * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) * oTable.fnClearTable(); * } ); */ this.fnClearTable = function( bRedraw ) { var api = this.api( true ).clear(); if ( bRedraw === undefined || bRedraw ) { api.draw(); } }; /** * The exact opposite of 'opening' a row, this function will close any rows which * are currently 'open'. * @param {node} nTr the table row to 'close' * @returns {int} 0 on success, or 1 if failed (can't find the row) * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable; * * // 'open' an information row when a row is clicked on * $('#example tbody tr').click( function () { * if ( oTable.fnIsOpen(this) ) { * oTable.fnClose( this ); * } else { * oTable.fnOpen( this, "Temporary row opened", "info_row" ); * } * } ); * * oTable = $('#example').dataTable(); * } ); */ this.fnClose = function( nTr ) { this.api( true ).row( nTr ).child.hide(); }; /** * Remove a row for the table * @param {mixed} target The index of the row from aoData to be deleted, or * the TR element you want to delete * @param {function|null} [callBack] Callback function * @param {bool} [redraw=true] Redraw the table or not * @returns {array} The row that was deleted * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Immediately remove the first row * oTable.fnDeleteRow( 0 ); * } ); */ this.fnDeleteRow = function( target, callback, redraw ) { var api = this.api( true ); var rows = api.rows( target ); var settings = rows.settings()[0]; var data = settings.aoData[ rows[0][0] ]; rows.remove(); if ( callback ) { callback.call( this, settings, data ); } if ( redraw === undefined || redraw ) { api.draw(); } return data; }; /** * Restore the table to it's original state in the DOM by removing all of DataTables * enhancements, alterations to the DOM structure of the table and event listeners. * @param {boolean} [remove=false] Completely remove the table from the DOM * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * // This example is fairly pointless in reality, but shows how fnDestroy can be used * var oTable = $('#example').dataTable(); * oTable.fnDestroy(); * } ); */ this.fnDestroy = function ( remove ) { this.api( true ).destroy( remove ); }; /** * Redraw the table * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) * oTable.fnDraw(); * } ); */ this.fnDraw = function( complete ) { // Note that this isn't an exact match to the old call to _fnDraw - it takes // into account the new data, but can old position. this.api( true ).draw( ! complete ); }; /** * Filter the input based on data * @param {string} sInput String to filter the table on * @param {int|null} [iColumn] Column to limit filtering to * @param {bool} [bRegex=false] Treat as regular expression or not * @param {bool} [bSmart=true] Perform smart filtering or not * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Sometime later - filter... * oTable.fnFilter( 'test string' ); * } ); */ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) { var api = this.api( true ); if ( iColumn === null || iColumn === undefined ) { api.search( sInput, bRegex, bSmart, bCaseInsensitive ); } else { api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); } api.draw(); }; /** * Get the data for the whole table, an individual row or an individual cell based on the * provided parameters. * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as * a TR node then the data source for the whole row will be returned. If given as a * TD/TH cell node then iCol will be automatically calculated and the data for the * cell returned. If given as an integer, then this is treated as the aoData internal * data index for the row (see fnGetPosition) and the data for that row used. * @param {int} [col] Optional column index that you want the data of. * @returns {array|object|string} If mRow is undefined, then the data for all rows is * returned. If mRow is defined, just data for that row, and is iCol is * defined, only data for the designated cell is returned. * @dtopt API * @deprecated Since v1.10 * * @example * // Row data * $(document).ready(function() { * oTable = $('#example').dataTable(); * * oTable.$('tr').click( function () { * var data = oTable.fnGetData( this ); * // ... do something with the array / object of data for the row * } ); * } ); * * @example * // Individual cell data * $(document).ready(function() { * oTable = $('#example').dataTable(); * * oTable.$('td').click( function () { * var sData = oTable.fnGetData( this ); * alert( 'The cell clicked on had the value of '+sData ); * } ); * } ); */ this.fnGetData = function( src, col ) { var api = this.api( true ); if ( src !== undefined ) { var type = src.nodeName ? src.nodeName.toLowerCase() : ''; return col !== undefined || type == 'td' || type == 'th' ? api.cell( src, col ).data() : api.row( src ).data(); } return api.data().toArray(); }; /** * Get an array of the TR nodes that are used in the table's body. Note that you will * typically want to use the '$' API method in preference to this as it is more * flexible. * @param {int} [iRow] Optional row index for the TR element you want * @returns {array|node} If iRow is undefined, returns an array of all TR elements * in the table's body, or iRow is defined, just the TR element requested. * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Get the nodes from the table * var nNodes = oTable.fnGetNodes( ); * } ); */ this.fnGetNodes = function( iRow ) { var api = this.api( true ); return iRow !== undefined ? api.row( iRow ).node() : api.rows().nodes().toArray(); }; /** * Get the array indexes of a particular cell from it's DOM element * and column index including hidden columns * @param {node} node this can either be a TR, TD or TH in the table's body * @returns {int} If nNode is given as a TR, then a single index is returned, or * if given as a cell, an array of [row index, column index (visible), * column index (all)] is given. * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * $('#example tbody td').click( function () { * // Get the position of the current data from the node * var aPos = oTable.fnGetPosition( this ); * * // Get the data array for this row * var aData = oTable.fnGetData( aPos[0] ); * * // Update the data array and return the value * aData[ aPos[1] ] = 'clicked'; * this.innerHTML = 'clicked'; * } ); * * // Init DataTables * oTable = $('#example').dataTable(); * } ); */ this.fnGetPosition = function( node ) { var api = this.api( true ); var nodeName = node.nodeName.toUpperCase(); if ( nodeName == 'TR' ) { return api.row( node ).index(); } else if ( nodeName == 'TD' || nodeName == 'TH' ) { var cell = api.cell( node ).index(); return [ cell.row, cell.columnVisible, cell.column ]; } return null; }; /** * Check to see if a row is 'open' or not. * @param {node} nTr the table row to check * @returns {boolean} true if the row is currently open, false otherwise * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable; * * // 'open' an information row when a row is clicked on * $('#example tbody tr').click( function () { * if ( oTable.fnIsOpen(this) ) { * oTable.fnClose( this ); * } else { * oTable.fnOpen( this, "Temporary row opened", "info_row" ); * } * } ); * * oTable = $('#example').dataTable(); * } ); */ this.fnIsOpen = function( nTr ) { return this.api( true ).row( nTr ).child.isShown(); }; /** * This function will place a new row directly after a row which is currently * on display on the page, with the HTML contents that is passed into the * function. This can be used, for example, to ask for confirmation that a * particular record should be deleted. * @param {node} nTr The table row to 'open' * @param {string|node|jQuery} mHtml The HTML to put into the row * @param {string} sClass Class to give the new TD cell * @returns {node} The row opened. Note that if the table row passed in as the * first parameter, is not found in the table, this method will silently * return. * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable; * * // 'open' an information row when a row is clicked on * $('#example tbody tr').click( function () { * if ( oTable.fnIsOpen(this) ) { * oTable.fnClose( this ); * } else { * oTable.fnOpen( this, "Temporary row opened", "info_row" ); * } * } ); * * oTable = $('#example').dataTable(); * } ); */ this.fnOpen = function( nTr, mHtml, sClass ) { return this.api( true ).row( nTr ).child( mHtml, sClass ).show(); }; /** * Change the pagination - provides the internal logic for pagination in a simple API * function. With this function you can have a DataTables table go to the next, * previous, first or last pages. * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" * or page number to jump to (integer), note that page 0 is the first page. * @param {bool} [bRedraw=true] Redraw the table or not * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * oTable.fnPageChange( 'next' ); * } ); */ this.fnPageChange = function ( mAction, bRedraw ) { var api = this.api( true ).page( mAction ); if ( bRedraw === undefined || bRedraw ) { api.draw(false); } }; /** * Show a particular column * @param {int} iCol The column whose display should be changed * @param {bool} bShow Show (true) or hide (false) the column * @param {bool} [bRedraw=true] Redraw the table or not * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Hide the second column after initialisation * oTable.fnSetColumnVis( 1, false ); * } ); */ this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) { var api = this.api( true ).column( iCol ).visible( bShow ); if ( bRedraw === undefined || bRedraw ) { api.columns.adjust().draw(); } }; /** * Get the settings for a particular table for external manipulation * @returns {object} DataTables settings object. See * {@link DataTable.models.oSettings} * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * var oSettings = oTable.fnSettings(); * * // Show an example parameter from the settings * alert( oSettings._iDisplayStart ); * } ); */ this.fnSettings = function() { return _fnSettingsFromNode( this[_ext.iApiIndex] ); }; /** * Sort the table by a particular column * @param {int} iCol the data index to sort on. Note that this will not match the * 'display index' if you have hidden data entries * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Sort immediately with columns 0 and 1 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); * } ); */ this.fnSort = function( aaSort ) { this.api( true ).order( aaSort ).draw(); }; /** * Attach a sort listener to an element for a given column * @param {node} nNode the element to attach the sort listener to * @param {int} iColumn the column that a click on this node will sort on * @param {function} [fnCallback] callback function when sort is run * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * * // Sort on column 1, when 'sorter' is clicked on * oTable.fnSortListener( document.getElementById('sorter'), 1 ); * } ); */ this.fnSortListener = function( nNode, iColumn, fnCallback ) { this.api( true ).order.listener( nNode, iColumn, fnCallback ); }; /** * Update a table cell or row - this method will accept either a single value to * update the cell with, an array of values with one element for each column or * an object in the same format as the original data source. The function is * self-referencing in order to make the multi column updates easier. * @param {object|array|string} mData Data to update the cell/row with * @param {node|int} mRow TR element you want to update or the aoData index * @param {int} [iColumn] The column to update, give as null or undefined to * update a whole row. * @param {bool} [bRedraw=true] Redraw the table or not * @param {bool} [bAction=true] Perform pre-draw actions or not * @returns {int} 0 on success, 1 on error * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row * } ); */ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) { var api = this.api( true ); if ( iColumn === undefined || iColumn === null ) { api.row( mRow ).data( mData ); } else { api.cell( mRow, iColumn ).data( mData ); } if ( bAction === undefined || bAction ) { api.columns.adjust(); } if ( bRedraw === undefined || bRedraw ) { api.draw(); } return 0; }; /** * Provide a common method for plug-ins to check the version of DataTables being used, in order * to ensure compatibility. * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the * formats "X" and "X.Y" are also acceptable. * @returns {boolean} true if this version of DataTables is greater or equal to the required * version, or false if this version of DataTales is not suitable * @method * @dtopt API * @deprecated Since v1.10 * * @example * $(document).ready(function() { * var oTable = $('#example').dataTable(); * alert( oTable.fnVersionCheck( '1.9.0' ) ); * } ); */ this.fnVersionCheck = _ext.fnVersionCheck; /* * This is really a good bit rubbish this method of exposing the internal methods * publicly... - To be fixed in 2.0 using methods on the prototype */ /** * Create a wrapper function for exporting an internal functions to an external API. * @param {string} fn API function name * @returns {function} wrapped function * @memberof DataTable#internal */ function _fnExternApiFunc (fn) { return function() { var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat( Array.prototype.slice.call(arguments) ); return DataTable.ext.internal[fn].apply( this, args ); }; } /** * Reference to internal functions for use by plug-in developers. Note that * these methods are references to internal functions and are considered to be * private. If you use these methods, be aware that they are liable to change * between versions. * @namespace */ this.oApi = this.internal = { _fnExternApiFunc: _fnExternApiFunc, _fnBuildAjax: _fnBuildAjax, _fnAjaxUpdate: _fnAjaxUpdate, _fnAjaxParameters: _fnAjaxParameters, _fnAjaxUpdateDraw: _fnAjaxUpdateDraw, _fnAjaxDataSrc: _fnAjaxDataSrc, _fnAddColumn: _fnAddColumn, _fnColumnOptions: _fnColumnOptions, _fnAdjustColumnSizing: _fnAdjustColumnSizing, _fnVisibleToColumnIndex: _fnVisibleToColumnIndex, _fnColumnIndexToVisible: _fnColumnIndexToVisible, _fnVisbleColumns: _fnVisbleColumns, _fnGetColumns: _fnGetColumns, _fnColumnTypes: _fnColumnTypes, _fnApplyColumnDefs: _fnApplyColumnDefs, _fnHungarianMap: _fnHungarianMap, _fnCamelToHungarian: _fnCamelToHungarian, _fnLanguageCompat: _fnLanguageCompat, _fnBrowserDetect: _fnBrowserDetect, _fnAddData: _fnAddData, _fnAddTr: _fnAddTr, _fnNodeToDataIndex: _fnNodeToDataIndex, _fnNodeToColumnIndex: _fnNodeToColumnIndex, _fnGetRowData: _fnGetRowData, _fnGetCellData: _fnGetCellData, _fnSetCellData: _fnSetCellData, _fnSplitObjNotation: _fnSplitObjNotation, _fnGetObjectDataFn: _fnGetObjectDataFn, _fnSetObjectDataFn: _fnSetObjectDataFn, _fnGetDataMaster: _fnGetDataMaster, _fnClearTable: _fnClearTable, _fnDeleteIndex: _fnDeleteIndex, _fnInvalidateRow: _fnInvalidateRow, _fnGetRowElements: _fnGetRowElements, _fnCreateTr: _fnCreateTr, _fnBuildHead: _fnBuildHead, _fnDrawHead: _fnDrawHead, _fnDraw: _fnDraw, _fnReDraw: _fnReDraw, _fnAddOptionsHtml: _fnAddOptionsHtml, _fnDetectHeader: _fnDetectHeader, _fnGetUniqueThs: _fnGetUniqueThs, _fnFeatureHtmlFilter: _fnFeatureHtmlFilter, _fnFilterComplete: _fnFilterComplete, _fnFilterCustom: _fnFilterCustom, _fnFilterColumn: _fnFilterColumn, _fnFilter: _fnFilter, _fnFilterCreateSearch: _fnFilterCreateSearch, _fnEscapeRegex: _fnEscapeRegex, _fnFilterData: _fnFilterData, _fnFeatureHtmlInfo: _fnFeatureHtmlInfo, _fnUpdateInfo: _fnUpdateInfo, _fnInfoMacros: _fnInfoMacros, _fnInitialise: _fnInitialise, _fnInitComplete: _fnInitComplete, _fnLengthChange: _fnLengthChange, _fnFeatureHtmlLength: _fnFeatureHtmlLength, _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate, _fnPageChange: _fnPageChange, _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing, _fnProcessingDisplay: _fnProcessingDisplay, _fnFeatureHtmlTable: _fnFeatureHtmlTable, _fnScrollDraw: _fnScrollDraw, _fnApplyToChildren: _fnApplyToChildren, _fnCalculateColumnWidths: _fnCalculateColumnWidths, _fnThrottle: _fnThrottle, _fnConvertToWidth: _fnConvertToWidth, _fnScrollingWidthAdjust: _fnScrollingWidthAdjust, _fnGetWidestNode: _fnGetWidestNode, _fnGetMaxLenString: _fnGetMaxLenString, _fnStringToCss: _fnStringToCss, _fnScrollBarWidth: _fnScrollBarWidth, _fnSortFlatten: _fnSortFlatten, _fnSort: _fnSort, _fnSortAria: _fnSortAria, _fnSortListener: _fnSortListener, _fnSortAttachListener: _fnSortAttachListener, _fnSortingClasses: _fnSortingClasses, _fnSortData: _fnSortData, _fnSaveState: _fnSaveState, _fnLoadState: _fnLoadState, _fnSettingsFromNode: _fnSettingsFromNode, _fnLog: _fnLog, _fnMap: _fnMap, _fnBindAction: _fnBindAction, _fnCallbackReg: _fnCallbackReg, _fnCallbackFire: _fnCallbackFire, _fnLengthOverflow: _fnLengthOverflow, _fnRenderer: _fnRenderer, _fnDataSource: _fnDataSource, _fnRowAttributes: _fnRowAttributes }; $.extend( DataTable.ext.internal, this.internal ); for ( var fn in DataTable.ext.internal ) { if ( fn ) { this[fn] = _fnExternApiFunc(fn); } } var _that = this; var emptyInit = options === undefined; var len = this.length; if ( emptyInit ) { options = {}; } this.each(function() { // For each initialisation we want to give it a clean initialisation // object that can be bashed around var o = {}; var oInit = len > 1 ? // optimisation for single table case _fnExtend( o, options, true ) : options; /*global oInit,_that,emptyInit*/ var i=0, iLen, j, jLen, k, kLen; var sId = this.getAttribute( 'id' ); var bInitHandedOff = false; var defaults = DataTable.defaults; /* Sanity check */ if ( this.nodeName.toLowerCase() != 'table' ) { _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); return; } /* Backwards compatibility for the defaults */ _fnCompatOpts( defaults ); _fnCompatCols( defaults.column ); /* Convert the camel-case defaults to Hungarian */ _fnCamelToHungarian( defaults, defaults, true ); _fnCamelToHungarian( defaults.column, defaults.column, true ); /* Setting up the initialisation object */ _fnCamelToHungarian( defaults, oInit ); /* Check to see if we are re-initialising a table */ var allSettings = DataTable.settings; for ( i=0, iLen=allSettings.length ; i').appendTo(this); } oSettings.nTHead = thead[0]; var tbody = $(this).children('tbody'); if ( tbody.length === 0 ) { tbody = $('').appendTo(this); } oSettings.nTBody = tbody[0]; var tfoot = $(this).children('tfoot'); if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { // If we are a scrolling table, and no footer has been given, then we need to create // a tfoot element for the caption element to be appended to tfoot = $('').appendTo(this); } if ( tfoot.length === 0 || tfoot.children().length === 0 ) { $(this).addClass( oSettings.oClasses.sNoFooter ); } else if ( tfoot.length > 0 ) { oSettings.nTFoot = tfoot[0]; _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); } /* Check if there is data passing into the constructor */ if ( oInit.aaData ) { for ( i=0 ; i 1 ) { value = init; isSet = true; } for ( var i=0, ien=this.length ; i 1 ) { value = init; isSet = true; } for ( var i=this.length-1 ; i>=0 ; i-- ) { if ( ! this.hasOwnProperty(i) ) { continue; } value = isSet ? fn( value, this[i], i, this ) : this[i]; isSet = true; } return value; }, reverse: __arrayProto.reverse, // Object with rows, columns and opts selector: null, shift: __arrayProto.shift, sort: __arrayProto.sort, // ? name - order? splice: __arrayProto.splice, toArray: function () { return __arrayProto.slice.call( this ); }, to$: function () { return $( this ); }, toJQuery: function () { return $( this ); }, unique: function () { return new _Api( this.context, _unique(this) ); }, unshift: __arrayProto.unshift }; _Api.extend = function ( scope, obj, ext ) { // Only extend API instances and static properties of the API if ( ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { return; } var i, ien, j, jen, struct, inner, methodScoping = function ( fn, struc ) { return function () { var ret = fn.apply( scope, arguments ); // Method extension _Api.extend( ret, ret, struc.methodExt ); return ret; }; }; for ( i=0, ien=ext.length ; i 0 ) { return ctx[0].json; } // else return undefined; } ); /** * Reload tables from the Ajax data source. Note that this function will * automatically re-draw the table when the remote data has been loaded. * * @param {boolean} [reset=true] Reset (default) or hold the current paging * position. A full re-sort and re-filter is performed when this method is * called, which is why the pagination reset is the default action. * @returns {DataTables.Api} this */ _api_register( 'ajax.reload()', function ( callback, resetPaging ) { return this.iterator( 'table', function (settings) { __reload( settings, resetPaging===false, callback ); } ); } ); /** * Get the current Ajax URL. Note that this returns the URL from the first * table in the current context. * * @return {string} Current Ajax source URL *//** * Set the Ajax URL. Note that this will set the URL for all tables in the * current context. * * @param {string} url URL to set. * @returns {DataTables.Api} this */ _api_register( 'ajax.url()', function ( url ) { var ctx = this.context; if ( url === undefined ) { // get if ( ctx.length === 0 ) { return undefined; } ctx = ctx[0]; return ctx.ajax ? $.isPlainObject( ctx.ajax ) ? ctx.ajax.url : ctx.ajax : ctx.sAjaxSource; } // set return this.iterator( 'table', function ( settings ) { if ( $.isPlainObject( settings.ajax ) ) { settings.ajax.url = url; } else { settings.ajax = url; } // No need to consider sAjaxSource here since DataTables gives priority // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any // value of `sAjaxSource` redundant. } ); } ); /** * Load data from the newly set Ajax URL. Note that this method is only * available when `ajax.url()` is used to set a URL. Additionally, this method * has the same effect as calling `ajax.reload()` but is provided for * convenience when setting a new URL. Like `ajax.reload()` it will * automatically redraw the table once the remote data has been loaded. * * @returns {DataTables.Api} this */ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { // Same as a reload, but makes sense to present it for easy access after a // url change return this.iterator( 'table', function ( ctx ) { __reload( ctx, resetPaging===false, callback ); } ); } ); var _selector_run = function ( selector, select ) { var out = [], res, a, i, ien, j, jen; if ( ! $.isArray( selector ) ) { selector = [ selector ]; } for ( i=0, ien=selector.length ; i 0 ) { // Assign the first element to the first item in the instance // and truncate the instance and context inst[0] = inst[i]; inst.length = 1; inst.context = [ inst.context[i] ]; return inst; } } // Not found - return an empty instance inst.length = 0; return inst; }; var _selector_row_indexes = function ( settings, opts ) { var i, ien, tmp, a=[], displayFiltered = settings.aiDisplay, displayMaster = settings.aiDisplayMaster; var search = opts.search, // none, applied, removed order = opts.order, // applied, current, index (original - compatibility with 1.9) page = opts.page; // all, current // Current page implies that order=current and fitler=applied, since it is // fairly senseless otherwise, regardless of what order and search actually // are if ( page == 'current' ) { for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i').find('td').html( r ).parent(); } $('td', r).addClass( k )[0].colSpan = _fnVisbleColumns( ctx ); rows.push( r[0] ); }; if ( $.isArray( data ) || data instanceof $ ) { for ( var i=0, ien=data.length ; i 0 ) { // On each draw, insert the required elements into the document table.on('draw.DT_details', function () { table.find('tbody tr').each( function () { // Look up the row index for each row and append open row var rowIdx = _fnNodeToDataIndex( settings, this ); var row = settings.aoData[ rowIdx ]; if ( row._detailsShow ) { row._details.insertAfter( this ); } } ); } ); // Column visibility change - update the colspan table.on( 'column-visibility.DT_details', function ( e, settings, idx, vis ) { // Update the colspan for the details rows (note, only if it already has // a colspan) var row, visible = _fnVisbleColumns( settings ); for ( var i=0, ien=settings.aoData.length ; i=0 count from left, <0 count from right) * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) * "{string}:name" - column name * "{string}" - jQuery selector on column header nodes * */ // can be an array of these items, comma separated list, or an array of comma // separated lists var __re_column_selector = /^(.*):(name|visIdx|visible)$/; var __column_selector = function ( settings, selector, opts ) { var columns = settings.aoColumns, names = _pluck( columns, 'sName' ), nodes = _pluck( columns, 'nTh' ); return _selector_run( selector, function ( s ) { var selInt = _intVal( s ); if ( s === '' ) { // All columns return _range( settings.aoColumns.length ); } else if ( selInt !== null ) { // Integer selector return [ selInt >= 0 ? selInt : // Count from left columns.length + selInt // Count from right (+ because its a negative value) ]; } else { var match = s.match( __re_column_selector ); if ( match ) { switch( match[2] ) { case 'visIdx': case 'visible': var idx = parseInt( match[1], 10 ); // Visible index given, convert to column index if ( idx < 0 ) { // Counting from the right var visColumns = $.map( columns, function (col,i) { return col.bVisible ? i : null; } ); return [ visColumns[ visColumns.length + idx ] ]; } // Counting from the left return [ _fnVisibleToColumnIndex( settings, idx ) ]; case 'name': // match by name. `names` is column index complete and in order return $.map( names, function (name, i) { return name === match[1] ? i : null; } ); } } else { // jQuery selector on the TH elements for the columns return $( nodes ) .filter( s ) .map( function () { return $.inArray( this, nodes ); // `nodes` is column index complete and in order } ) .toArray(); } } } ); }; var __setColumnVis = function ( settings, column, vis ) { var cols = settings.aoColumns, col = cols[ column ], data = settings.aoData, row, cells, i, ien, tr; // Get if ( vis === undefined ) { return col.bVisible; } // Set // No change if ( col.bVisible === vis ) { return; } if ( vis ) { // Insert column // Need to decide if we should use appendChild or insertBefore var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 ); for ( i=0, ien=data.length ; i iThat; } return true; }; /** * Check if a `` node is a DataTable table already or not. * * @param {node|jquery|string} table Table node, jQuery object or jQuery * selector for the table to test. Note that if more than more than one * table is passed on, only the first will be checked * @returns {boolean} true the table given is a DataTable, or false otherwise * @static * @dtopt API-Static * * @example * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { * $('#example').dataTable(); * } */ DataTable.isDataTable = DataTable.fnIsDataTable = function ( table ) { var t = $(table).get(0); var is = false; $.each( DataTable.settings, function (i, o) { if ( o.nTable === t || o.nScrollHead === t || o.nScrollFoot === t ) { is = true; } } ); return is; }; /** * Get all DataTable tables that have been initialised - optionally you can * select to get only currently visible tables. * * @param {boolean} [visible=false] Flag to indicate if you want all (default) * or visible tables only. * @returns {array} Array of `table` nodes (not DataTable instances) which are * DataTables * @static * @dtopt API-Static * * @example * $.each( $.fn.dataTable.tables(true), function () { * $(table).DataTable().columns.adjust(); * } ); */ DataTable.tables = DataTable.fnTables = function ( visible ) { return jQuery.map( DataTable.settings, function (o) { if ( !visible || (visible && $(o.nTable).is(':visible')) ) { return o.nTable; } } ); }; /** * */ _api_register( '$()', function ( selector, opts ) { var rows = this.rows( opts ).nodes(), // Get all rows jqRows = $(rows); return $( [].concat( jqRows.filter( selector ).toArray(), jqRows.find( selector ).toArray() ) ); } ); // jQuery functions to operate on the tables $.each( [ 'on', 'one', 'off' ], function (i, key) { _api_register( key+'()', function ( /* event, handler */ ) { var args = Array.prototype.slice.call(arguments); // Add the `dt` namespace automatically if it isn't already present if ( args[0].indexOf( '.dt' ) === -1 ) { args[0] += '.dt'; } var inst = $( this.tables().nodes() ); inst[key].apply( inst, args ); return this; } ); } ); _api_register( 'clear()', function () { return this.iterator( 'table', function ( settings ) { _fnClearTable( settings ); } ); } ); _api_register( 'settings()', function () { return new _Api( this.context, this.context ); } ); _api_register( 'data()', function () { return this.iterator( 'table', function ( settings ) { return _pluck( settings.aoData, '_aData' ); } ).flatten(); } ); _api_register( 'destroy()', function ( remove ) { remove = remove || false; return this.iterator( 'table', function ( settings ) { var orig = settings.nTableWrapper.parentNode; var classes = settings.oClasses; var table = settings.nTable; var tbody = settings.nTBody; var thead = settings.nTHead; var tfoot = settings.nTFoot; var jqTable = $(table); var jqTbody = $(tbody); var jqWrapper = $(settings.nTableWrapper); var rows = $.map( settings.aoData, function (r) { return r.nTr; } ); var i, ien; // Flag to note that the table is currently being destroyed - no action // should be taken settings.bDestroying = true; // Fire off the destroy callbacks for plug-ins etc _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] ); // If not being removed from the document, make all columns visible if ( ! remove ) { new _Api( settings ).columns().visible( true ); } // Blitz all DT events jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT'); $(window).unbind('.DT-'+settings.sInstance); // When scrolling we had to break the table up - restore it if ( table != thead.parentNode ) { jqTable.children('thead').remove(); jqTable.append( thead ); } if ( tfoot && table != tfoot.parentNode ) { jqTable.children('tfoot').remove(); jqTable.append( tfoot ); } // Remove the DataTables generated nodes, events and classes jqTable.remove(); jqWrapper.remove(); settings.aaSorting = []; settings.aaSortingFixed = []; _fnSortingClasses( settings ); $( rows ).removeClass( settings.asStripeClasses.join(' ') ); $('th, td', thead).removeClass( classes.sSortable+' '+ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone ); if ( settings.bJUI ) { $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).remove(); $('th, td', thead).each( function () { var wrapper = $('div.'+classes.sSortJUIWrapper, this); $(this).append( wrapper.contents() ); wrapper.remove(); } ); } if ( ! remove ) { // insertBefore acts like appendChild if !arg[1] orig.insertBefore( table, settings.nTableReinsertBefore ); } // Add the TR elements back into the table in their original order jqTbody.children().detach(); jqTbody.append( rows ); // Restore the width of the original table - was read from the style property, // so we can restore directly to that jqTable .css( 'width', settings.sDestroyWidth ) .removeClass( classes.sTable ); // If the were originally stripe classes - then we add them back here. // Note this is not fool proof (for example if not all rows had stripe // classes - but it's a good effort without getting carried away ien = settings.asDestroyStripes.length; if ( ien ) { jqTbody.children().each( function (i) { $(this).addClass( settings.asDestroyStripes[i % ien] ); } ); } /* Remove the settings object from the settings array */ var idx = $.inArray( settings, DataTable.settings ); if ( idx !== -1 ) { DataTable.settings.splice( idx, 1 ); } } ); } ); /** * Version string for plug-ins to check compatibility. Allowed format is * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used * only for non-release builds. See http://semver.org/ for more information. * @member * @type string * @default Version number */ DataTable.version = "1.10.0-dev"; /** * Private data store, containing all of the settings objects that are * created for the tables on a given page. * * Note that the `DataTable.settings` object is aliased to * `jQuery.fn.dataTableExt` through which it may be accessed and * manipulated, or `jQuery.fn.dataTable.settings`. * @member * @type array * @default [] * @private */ DataTable.settings = []; /** * Object models container, for the various models that DataTables has * available to it. These models define the objects that are used to hold * the active state and configuration of the table. * @namespace */ DataTable.models = {}; /** * Template object for the way in which DataTables holds information about * search information for the global filter and individual column filters. * @namespace */ DataTable.models.oSearch = { /** * Flag to indicate if the filtering should be case insensitive or not * @type boolean * @default true */ "bCaseInsensitive": true, /** * Applied search term * @type string * @default Empty string */ "sSearch": "", /** * Flag to indicate if the search term should be interpreted as a * regular expression (true) or not (false) and therefore and special * regex characters escaped. * @type boolean * @default false */ "bRegex": false, /** * Flag to indicate if DataTables is to use its smart filtering or not. * @type boolean * @default true */ "bSmart": true }; /** * Template object for the way in which DataTables holds information about * each individual row. This is the object format used for the settings * aoData array. * @namespace */ DataTable.models.oRow = { /** * TR element for the row * @type node * @default null */ "nTr": null, /** * Array of TD elements for each row. This is null until the row has been * created. * @type array nodes * @default [] */ "anCells": null, /** * Data object from the original data source for the row. This is either * an array if using the traditional form of DataTables, or an object if * using mData options. The exact type will depend on the passed in * data from the data source, or will be an array if using DOM a data * source. * @type array|object * @default [] */ "_aData": [], /** * Sorting data cache - this array is ostensibly the same length as the * number of columns (although each index is generated only as it is * needed), and holds the data that is used for sorting each column in the * row. We do this cache generation at the start of the sort in order that * the formatting of the sort data need be done only once for each cell * per sort. This array should not be read from or written to by anything * other than the master sorting methods. * @type array * @default null * @private */ "_aSortData": null, /** * Per cell filtering data cache. As per the sort data cache, used to * increase the performance of the filtering in DataTables * @type array * @default null * @private */ "_aFilterData": null, /** * Filtering data cache. This is the same as the cell filtering cache, but * in this case a string rather than an array. This is easily computed with * a join on `_aFilterData`, but is provided as a cache so the join isn't * needed on every search (memory traded for performance) * @type array * @default null * @private */ "_sFilterRow": null, /** * Cache of the class name that DataTables has applied to the row, so we * can quickly look at this variable rather than needing to do a DOM check * on className for the nTr property. * @type string * @default Empty string * @private */ "_sRowStripe": "", /** * Denote if the original data source was from the DOM, or the data source * object. This is used for invalidating data, so DataTables can * automatically read data from the original source, unless uninstructed * otherwise. * @type string * @default null * @private */ "src": null }; /** * Template object for the column information object in DataTables. This object * is held in the settings aoColumns array and contains all the information that * DataTables needs about each individual column. * * Note that this object is related to {@link DataTable.defaults.column} * but this one is the internal data store for DataTables's cache of columns. * It should NOT be manipulated outside of DataTables. Any configuration should * be done through the initialisation options. * @namespace */ DataTable.models.oColumn = { /** * A list of the columns that sorting should occur on when this column * is sorted. That this property is an array allows multi-column sorting * to be defined for a column (for example first name / last name columns * would benefit from this). The values are integers pointing to the * columns to be sorted on (typically it will be a single integer pointing * at itself, but that doesn't need to be the case). * @type array */ "aDataSort": null, /** * Define the sorting directions that are applied to the column, in sequence * as the column is repeatedly sorted upon - i.e. the first value is used * as the sorting direction when the column if first sorted (clicked on). * Sort it again (click again) and it will move on to the next index. * Repeat until loop. * @type array */ "asSorting": null, /** * Flag to indicate if the column is searchable, and thus should be included * in the filtering or not. * @type boolean */ "bSearchable": null, /** * Flag to indicate if the column is sortable or not. * @type boolean */ "bSortable": null, /** * Flag to indicate if the column is currently visible in the table or not * @type boolean */ "bVisible": null, /** * Store for manual type assignment using the `column.type` option. This * is held in store so we can manipulate the column's `sType` property. * @type string * @default null * @private */ "_sManualType": null, /** * Flag to indicate if HTML5 data attributes should be used as the data * source for filtering or sorting. True is either are. * @type boolean * @default false * @private */ "_bAttrSrc": false, /** * Developer definable function that is called whenever a cell is created (Ajax source, * etc) or processed for input (DOM source). This can be used as a compliment to mRender * allowing you to modify the DOM element (add background colour for example) when the * element is available. * @type function * @param {element} nTd The TD node that has been created * @param {*} sData The Data for the cell * @param {array|object} oData The data for the whole row * @param {int} iRow The row index for the aoData data store * @default null */ "fnCreatedCell": null, /** * Function to get data from a cell in a column. You should never * access data directly through _aData internally in DataTables - always use * the method attached to this property. It allows mData to function as * required. This function is automatically assigned by the column * initialisation method * @type function * @param {array|object} oData The data array/object for the array * (i.e. aoData[]._aData) * @param {string} sSpecific The specific data type you want to get - * 'display', 'type' 'filter' 'sort' * @returns {*} The data for the cell from the given row's data * @default null */ "fnGetData": null, /** * Function to set data for a cell in the column. You should never * set the data directly to _aData internally in DataTables - always use * this method. It allows mData to function as required. This function * is automatically assigned by the column initialisation method * @type function * @param {array|object} oData The data array/object for the array * (i.e. aoData[]._aData) * @param {*} sValue Value to set * @default null */ "fnSetData": null, /** * Property to read the value for the cells in the column from the data * source array / object. If null, then the default content is used, if a * function is given then the return from the function is used. * @type function|int|string|null * @default null */ "mData": null, /** * Partner property to mData which is used (only when defined) to get * the data - i.e. it is basically the same as mData, but without the * 'set' option, and also the data fed to it is the result from mData. * This is the rendering method to match the data method of mData. * @type function|int|string|null * @default null */ "mRender": null, /** * Unique header TH/TD element for this column - this is what the sorting * listener is attached to (if sorting is enabled.) * @type node * @default null */ "nTh": null, /** * Unique footer TH/TD element for this column (if there is one). Not used * in DataTables as such, but can be used for plug-ins to reference the * footer for each column. * @type node * @default null */ "nTf": null, /** * The class to apply to all TD elements in the table's TBODY for the column * @type string * @default null */ "sClass": null, /** * When DataTables calculates the column widths to assign to each column, * it finds the longest string in each column and then constructs a * temporary table and reads the widths from that. The problem with this * is that "mmm" is much wider then "iiii", but the latter is a longer * string - thus the calculation can go wrong (doing it properly and putting * it into an DOM object and measuring that is horribly(!) slow). Thus as * a "work around" we provide this option. It will append its value to the * text that is found to be the longest string for the column - i.e. padding. * @type string */ "sContentPadding": null, /** * Allows a default value to be given for a column's data, and will be used * whenever a null data source is encountered (this can be because mData * is set to null, or because the data source itself is null). * @type string * @default null */ "sDefaultContent": null, /** * Name for the column, allowing reference to the column by name as well as * by index (needs a lookup to work by name). * @type string */ "sName": null, /** * Custom sorting data type - defines which of the available plug-ins in * afnSortData the custom sorting will use - if any is defined. * @type string * @default std */ "sSortDataType": 'std', /** * Class to be applied to the header element when sorting on this column * @type string * @default null */ "sSortingClass": null, /** * Class to be applied to the header element when sorting on this column - * when jQuery UI theming is used. * @type string * @default null */ "sSortingClassJUI": null, /** * Title of the column - what is seen in the TH element (nTh). * @type string */ "sTitle": null, /** * Column sorting and filtering type * @type string * @default null */ "sType": null, /** * Width of the column * @type string * @default null */ "sWidth": null, /** * Width of the column when it was first "encountered" * @type string * @default null */ "sWidthOrig": null }; /* * Developer note: The properties of the object below are given in Hungarian * notation, that was used as the interface for DataTables prior to v1.10, however * from v1.10 onwards the primary interface is camel case. In order to avoid * breaking backwards compatibility utterly with this change, the Hungarian * version is still, internally the primary interface, but is is not documented * - hence the @name tags in each doc comment. This allows a Javascript function * to create a map from Hungarian notation to camel case (going the other direction * would require each property to be listed, which would at around 3K to the size * of DataTables, while this method is about a 0.5K hit. * * Ultimately this does pave the way for Hungarian notation to be dropped * completely, but that is a massive amount of work and will break current * installs (therefore is on-hold until v2). */ /** * Initialisation options that can be given to DataTables at initialisation * time. * @namespace */ DataTable.defaults = { /** * An array of data to use for the table, passed in at initialisation which * will be used in preference to any data which is already in the DOM. This is * particularly useful for constructing tables purely in Javascript, for * example with a custom Ajax call. * @type array * @default null * * @dtopt Option * @name DataTable.defaults.data * * @example * // Using a 2D array data source * $(document).ready( function () { * $('#example').dataTable( { * "data": [ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'], * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'], * ], * "columns": [ * { "title": "Engine" }, * { "title": "Browser" }, * { "title": "Platform" }, * { "title": "Version" }, * { "title": "Grade" } * ] * } ); * } ); * * @example * // Using an array of objects as a data source (`data`) * $(document).ready( function () { * $('#example').dataTable( { * "data": [ * { * "engine": "Trident", * "browser": "Internet Explorer 4.0", * "platform": "Win 95+", * "version": 4, * "grade": "X" * }, * { * "engine": "Trident", * "browser": "Internet Explorer 5.0", * "platform": "Win 95+", * "version": 5, * "grade": "C" * } * ], * "columns": [ * { "title": "Engine", "data": "engine" }, * { "title": "Browser", "data": "browser" }, * { "title": "Platform", "data": "platform" }, * { "title": "Version", "data": "version" }, * { "title": "Grade", "data": "grade" } * ] * } ); * } ); */ "aaData": null, /** * If ordering is enabled, then DataTables will perform a first pass sort on * initialisation. You can define which column(s) the sort is performed * upon, and the sorting direction, with this variable. The `sorting` array * should contain an array for each column to be sorted initially containing * the column's index and a direction string ('asc' or 'desc'). * @type array * @default [[0,'asc']] * * @dtopt Option * @name DataTable.defaults.order * * @example * // Sort by 3rd column first, and then 4th column * $(document).ready( function() { * $('#example').dataTable( { * "order": [[2,'asc'], [3,'desc']] * } ); * } ); * * // No initial sorting * $(document).ready( function() { * $('#example').dataTable( { * "order": [] * } ); * } ); */ "aaSorting": [[0,'asc']], /** * This parameter is basically identical to the `sorting` parameter, but * cannot be overridden by user interaction with the table. What this means * is that you could have a column (visible or hidden) which the sorting * will always be forced on first - any sorting after that (from the user) * will then be performed as required. This can be useful for grouping rows * together. * @type array * @default null * * @dtopt Option * @name DataTable.defaults.orderFixed * * @example * $(document).ready( function() { * $('#example').dataTable( { * "orderFixed": [[0,'asc']] * } ); * } ) */ "aaSortingFixed": [], /** * DataTables can be instructed to load data to display in the table from a * Ajax source. This option defines how that Ajax call is made and where to. * * The `ajax` property has three different modes of operation, depending on * how it is defined. These are: * * * `string` - Set the URL from where the data should be loaded from. * * `object` - Define properties for `jQuery.ajax`. * * `function` - Custom data get function * * `string` * -------- * * As a string, the `ajax` property simply defines the URL from which * DataTables will load data. * * `object` * -------- * * As an object, the parameters in the object are passed to * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control * of the Ajax request. DataTables has a number of default parameters which * you can override using this option. Please refer to the jQuery * documentation for a full description of the options available, although * the following parameters provide additional options in DataTables or * require special consideration: * * * `data` - As with jQuery, `data` can be provided as an object, but it * can also be used as a function to manipulate the data DataTables sends * to the server. The function takes a single parameter, an object of * parameters with the values that DataTables has readied for sending. An * object may be returned which will be merged into the DataTables * defaults, or you can add the items to the object that was passed in and * not return anything from the function. This supersedes `fnServerParams` * from DataTables 1.9-. * * * `dataSrc` - By default DataTables will look for the property `data` (or * `aaData` for compatibility with DataTables 1.9-) when obtaining data * from an Ajax source or for server-side processing - this parameter * allows that property to be changed. You can use Javascript dotted * object notation to get a data source for multiple levels of nesting, or * it my be used as a function. As a function it takes a single parameter, * the JSON returned from the server, which can be manipulated as * required, with the returned value being that used by DataTables as the * data source for the table. This supersedes `sAjaxDataProp` from * DataTables 1.9-. * * * `success` - Should not be overridden it is used internally in * DataTables. To manipulate / transform the data returned by the server * use `ajax.dataSrc`, or use `ajax` as a function (see below). * * `function` * ---------- * * As a function, making the Ajax call is left up to yourself allowing * complete control of the Ajax request. Indeed, if desired, a method other * than Ajax could be used to obtain the required data, such as Web storage * or an AIR database. * * The function is given four parameters and no return is required. The * parameters are: * * 1. _object_ - Data to send to the server * 2. _function_ - Callback function that must be executed when the required * data has been obtained. That data should be passed into the callback * as the only parameter * 3. _object_ - DataTables settings object for the table * * Note that this supersedes `fnServerData` from DataTables 1.9-. * * @type string|object|function * @default null * * @dtopt Option * @name DataTable.defaults.ajax * @since 1.10.0 * * @example * // Get JSON data from a file via Ajax. * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default). * $('#example').dataTable( { * "ajax": "data.json" * } ); * * @example * // Get JSON data from a file via Ajax, using `dataSrc` to change * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`) * $('#example').dataTable( { * "ajax": { * "url": "data.json", * "dataSrc": "tableData" * } * } ); * * @example * // Get JSON data from a file via Ajax, using `dataSrc` to read data * // from a plain array rather than an array in an object * $('#example').dataTable( { * "ajax": { * "url": "data.json", * "dataSrc": "" * } * } ); * * @example * // Manipulate the data returned from the server - add a link to data * // (note this can, should, be done using `render` for the column - this * // is just a simple example of how the data can be manipulated). * $('#example').dataTable( { * "ajax": { * "url": "data.json", * "dataSrc": function ( json ) { * for ( var i=0, ien=json.length ; iView message'; * } * return json; * } * } * } ); * * @example * // Add data to the request * $('#example').dataTable( { * "ajax": { * "url": "data.json", * "data": function ( d ) { * return { * "extra_search": $('#extra').val() * }; * } * } * } ); * * @example * // Send request as POST * $('#example').dataTable( { * "ajax": { * "url": "data.json", * "type": "POST" * } * } ); * * @example * // Get the data from localStorage (could interface with a form for * // adding, editing and removing rows). * $('#example').dataTable( { * "ajax": function (data, callback, settings) { * callback( * JSON.parse( localStorage.getItem('dataTablesData') ) * ); * } * } ); */ "ajax": null, /** * This parameter allows you to readily specify the entries in the length drop * down menu that DataTables shows when pagination is enabled. It can be * either a 1D array of options which will be used for both the displayed * option and the value, or a 2D array which will use the array in the first * position as the value, and the array in the second position as the * displayed options (useful for language strings such as 'All'). * * Note that the `pageLength` property will be automatically set to the * first value given in this array, unless `pageLength` is also provided. * @type array * @default [ 10, 25, 50, 100 ] * * @dtopt Option * @name DataTable.defaults.lengthMenu * * @example * $(document).ready( function() { * $('#example').dataTable( { * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]] * } ); * } ); */ "aLengthMenu": [ 10, 25, 50, 100 ], /** * The `columns` option in the initialisation parameter allows you to define * details about the way individual columns behave. For a full list of * column options that can be set, please see * {@link DataTable.defaults.column}. Note that if you use `columns` to * define your columns, you must have an entry in the array for every single * column that you have in your table (these can be null if you don't which * to specify any options). * @member * * @name DataTable.defaults.column */ "aoColumns": null, /** * Very similar to `columns`, `columnDefs` allows you to target a specific * column, multiple columns, or all columns, using the `targets` property of * each object in the array. This allows great flexibility when creating * tables, as the `columnDefs` arrays can be of any length, targeting the * columns you specifically want. `columnDefs` may use any of the column * options available: {@link DataTable.defaults.column}, but it _must_ * have `targets` defined in each object in the array. Values in the `targets` * array may be: *
            *
          • a string - class name will be matched on the TH for the column
          • *
          • 0 or a positive integer - column index counting from the left
          • *
          • a negative integer - column index counting from the right
          • *
          • the string "_all" - all columns (i.e. assign a default)
          • *
          * @member * * @name DataTable.defaults.columnDefs */ "aoColumnDefs": null, /** * Basically the same as `search`, this parameter defines the individual column * filtering state at initialisation time. The array must be of the same size * as the number of columns, and each element be an object with the parameters * `search` and `escapeRegex` (the latter is optional). 'null' is also * accepted and the default will be used. * @type array * @default [] * * @dtopt Option * @name DataTable.defaults.searchCols * * @example * $(document).ready( function() { * $('#example').dataTable( { * "searchCols": [ * null, * { "search": "My filter" }, * null, * { "search": "^[0-9]", "escapeRegex": false } * ] * } ); * } ) */ "aoSearchCols": [], /** * An array of CSS classes that should be applied to displayed rows. This * array may be of any length, and DataTables will apply each class * sequentially, looping when required. * @type array * @default null Will take the values determined by the `oClasses.stripe*` * options * * @dtopt Option * @name DataTable.defaults.stripeClasses * * @example * $(document).ready( function() { * $('#example').dataTable( { * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ] * } ); * } ) */ "asStripeClasses": null, /** * Enable or disable automatic column width calculation. This can be disabled * as an optimisation (it takes some time to calculate the widths) if the * tables widths are passed in using `columns`. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.autoWidth * * @example * $(document).ready( function () { * $('#example').dataTable( { * "autoWidth": false * } ); * } ); */ "bAutoWidth": true, /** * Deferred rendering can provide DataTables with a huge speed boost when you * are using an Ajax or JS data source for the table. This option, when set to * true, will cause DataTables to defer the creation of the table elements for * each row until they are needed for a draw - saving a significant amount of * time. * @type boolean * @default false * * @dtopt Features * @name DataTable.defaults.deferRender * * @example * $(document).ready( function() { * $('#example').dataTable( { * "ajax": "sources/arrays.txt", * "deferRender": true * } ); * } ); */ "bDeferRender": false, /** * Replace a DataTable which matches the given selector and replace it with * one which has the properties of the new initialisation object passed. If no * table matches the selector, then the new DataTable will be constructed as * per normal. * @type boolean * @default false * * @dtopt Options * @name DataTable.defaults.destroy * * @example * $(document).ready( function() { * $('#example').dataTable( { * "srollY": "200px", * "paginate": false * } ); * * // Some time later.... * $('#example').dataTable( { * "filter": false, * "destroy": true * } ); * } ); */ "bDestroy": false, /** * Enable or disable filtering of data. Filtering in DataTables is "smart" in * that it allows the end user to input multiple words (space separated) and * will match a row containing those words, even if not in the order that was * specified (this allow matching across multiple columns). Note that if you * wish to use filtering in DataTables this must remain 'true' - to remove the * default filtering input box and retain filtering abilities, please use * {@link DataTable.defaults.dom}. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.searching * * @example * $(document).ready( function () { * $('#example').dataTable( { * "searching": false * } ); * } ); */ "bFilter": true, /** * Enable or disable the table information display. This shows information * about the data that is currently visible on the page, including information * about filtered data if that action is being performed. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.info * * @example * $(document).ready( function () { * $('#example').dataTable( { * "info": false * } ); * } ); */ "bInfo": true, /** * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some * slightly different and additional mark-up from what DataTables has * traditionally used). * @type boolean * @default false * * @dtopt Features * @name DataTable.defaults.jQueryUI * * @example * $(document).ready( function() { * $('#example').dataTable( { * "jQueryUI": true * } ); * } ); */ "bJQueryUI": false, /** * Allows the end user to select the size of a formatted page from a select * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`). * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.lengthChange * * @example * $(document).ready( function () { * $('#example').dataTable( { * "lengthChange": false * } ); * } ); */ "bLengthChange": true, /** * Enable or disable pagination. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.paging * * @example * $(document).ready( function () { * $('#example').dataTable( { * "paging": false * } ); * } ); */ "bPaginate": true, /** * Enable or disable the display of a 'processing' indicator when the table is * being processed (e.g. a sort). This is particularly useful for tables with * large amounts of data where it can take a noticeable amount of time to sort * the entries. * @type boolean * @default false * * @dtopt Features * @name DataTable.defaults.processing * * @example * $(document).ready( function () { * $('#example').dataTable( { * "processing": true * } ); * } ); */ "bProcessing": false, /** * Retrieve the DataTables object for the given selector. Note that if the * table has already been initialised, this parameter will cause DataTables * to simply return the object that has already been set up - it will not take * account of any changes you might have made to the initialisation object * passed to DataTables (setting this parameter to true is an acknowledgement * that you understand this). `destroy` can be used to reinitialise a table if * you need. * @type boolean * @default false * * @dtopt Options * @name DataTable.defaults.retrieve * * @example * $(document).ready( function() { * initTable(); * tableActions(); * } ); * * function initTable () * { * return $('#example').dataTable( { * "scrollY": "200px", * "paginate": false, * "retrieve": true * } ); * } * * function tableActions () * { * var table = initTable(); * // perform API operations with oTable * } */ "bRetrieve": false, /** * When vertical (y) scrolling is enabled, DataTables will force the height of * the table's viewport to the given height at all times (useful for layout). * However, this can look odd when filtering data down to a small data set, * and the footer is left "floating" further down. This parameter (when * enabled) will cause DataTables to collapse the table's viewport down when * the result set will fit within the given Y height. * @type boolean * @default false * * @dtopt Options * @name DataTable.defaults.scrollCollapse * * @example * $(document).ready( function() { * $('#example').dataTable( { * "scrollY": "200", * "scrollCollapse": true * } ); * } ); */ "bScrollCollapse": false, /** * Configure DataTables to use server-side processing. Note that the * `ajax` parameter must also be given in order to give DataTables a * source to obtain the required data for each draw. * @type boolean * @default false * * @dtopt Features * @dtopt Server-side * @name DataTable.defaults.serverSide * * @example * $(document).ready( function () { * $('#example').dataTable( { * "serverSide": true, * "ajax": "xhr.php" * } ); * } ); */ "bServerSide": false, /** * Enable or disable sorting of columns. Sorting of individual columns can be * disabled by the `sortable` option for each column. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.ordering * * @example * $(document).ready( function () { * $('#example').dataTable( { * "ordering": false * } ); * } ); */ "bSort": true, /** * Enable or display DataTables' ability to sort multiple columns at the * same time (activated by shift-click by the user). * @type boolean * @default true * * @dtopt Options * @name DataTable.defaults.orderMulti * * @example * // Disable multiple column sorting ability * $(document).ready( function () { * $('#example').dataTable( { * "orderMulti": false * } ); * } ); */ "bSortMulti": true, /** * Allows control over whether DataTables should use the top (true) unique * cell that is found for a single column, or the bottom (false - default). * This is useful when using complex headers. * @type boolean * @default false * * @dtopt Options * @name DataTable.defaults.orderCellsTop * * @example * $(document).ready( function() { * $('#example').dataTable( { * "orderCellsTop": true * } ); * } ); */ "bSortCellsTop": false, /** * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and * `sorting\_3` to the columns which are currently being sorted on. This is * presented as a feature switch as it can increase processing time (while * classes are removed and added) so for large data sets you might want to * turn this off. * @type boolean * @default true * * @dtopt Features * @name DataTable.defaults.orderClasses * * @example * $(document).ready( function () { * $('#example').dataTable( { * "orderClasses": false * } ); * } ); */ "bSortClasses": true, /** * Enable or disable state saving. When enabled HTML5 `localStorage` will be * used to save table display information such as pagination information, * display length, filtering and sorting. As such when the end user reloads * the page the display display will match what thy had previously set up. * * Due to the use of `localStorage` the default state saving is not supported * in IE6 or 7. If state saving is required in those browsers, use * `stateSaveCallback` to provide a storage solution such as cookies. * @type boolean * @default false * * @dtopt Features * @name DataTable.defaults.stateSave * * @example * $(document).ready( function () { * $('#example').dataTable( { * "stateSave": true * } ); * } ); */ "bStateSave": false, /** * This function is called when a TR element is created (and all TD child * elements have been inserted), or registered if using a DOM source, allowing * manipulation of the TR element (adding classes etc). * @type function * @param {node} row "TR" element for the current row * @param {array} data Raw data array for this row * @param {int} dataIndex The index of this row in the internal aoData array * * @dtopt Callbacks * @name DataTable.defaults.createdRow * * @example * $(document).ready( function() { * $('#example').dataTable( { * "createdRow": function( row, data, dataIndex ) { * // Bold the grade for all 'A' grade browsers * if ( data[4] == "A" ) * { * $('td:eq(4)', row).html( 'A' ); * } * } * } ); * } ); */ "fnCreatedRow": null, /** * This function is called on every 'draw' event, and allows you to * dynamically modify any aspect you want about the created DOM. * @type function * @param {object} settings DataTables settings object * * @dtopt Callbacks * @name DataTable.defaults.drawCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "drawCallback": function( settings ) { * alert( 'DataTables has redrawn the table' ); * } * } ); * } ); */ "fnDrawCallback": null, /** * Identical to fnHeaderCallback() but for the table footer this function * allows you to modify the table footer on every 'draw' event. * @type function * @param {node} foot "TR" element for the footer * @param {array} data Full table data (as derived from the original HTML) * @param {int} start Index for the current display starting point in the * display array * @param {int} end Index for the current display ending point in the * display array * @param {array int} display Index array to translate the visual position * to the full data array * * @dtopt Callbacks * @name DataTable.defaults.footerCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "footerCallback": function( tfoot, data, start, end, display ) { * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start; * } * } ); * } ) */ "fnFooterCallback": null, /** * When rendering large numbers in the information element for the table * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers * to have a comma separator for the 'thousands' units (e.g. 1 million is * rendered as "1,000,000") to help readability for the end user. This * function will override the default method DataTables uses. * @type function * @member * @param {int} toFormat number to be formatted * @returns {string} formatted string for DataTables to show the number * * @dtopt Callbacks * @name DataTable.defaults.formatNumber * * @example * // Format a number using a single quote for the separator (note that * // this can also be done with the language.infoThousands option) * $(document).ready( function() { * $('#example').dataTable( { * "formatNumber": function ( toFormat ) { * return toFormat.toString().replace( * /\B(?=(\d{3})+(?!\d))/g, "'" * ); * }; * } ); * } ); */ "fnFormatNumber": function ( toFormat ) { return toFormat.toString().replace( /\B(?=(\d{3})+(?!\d))/g, this.oLanguage.sInfoThousands ); }, /** * This function is called on every 'draw' event, and allows you to * dynamically modify the header row. This can be used to calculate and * display useful information about the table. * @type function * @param {node} head "TR" element for the header * @param {array} data Full table data (as derived from the original HTML) * @param {int} start Index for the current display starting point in the * display array * @param {int} end Index for the current display ending point in the * display array * @param {array int} display Index array to translate the visual position * to the full data array * * @dtopt Callbacks * @name DataTable.defaults.headerCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "fheaderCallback": function( head, data, start, end, display ) { * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records"; * } * } ); * } ) */ "fnHeaderCallback": null, /** * The information element can be used to convey information about the current * state of the table. Although the internationalisation options presented by * DataTables are quite capable of dealing with most customisations, there may * be times where you wish to customise the string further. This callback * allows you to do exactly that. * @type function * @param {object} oSettings DataTables settings object * @param {int} start Starting position in data for the draw * @param {int} end End position in data for the draw * @param {int} max Total number of rows in the table (regardless of * filtering) * @param {int} total Total number of rows in the data set, after filtering * @param {string} pre The string that DataTables has formatted using it's * own rules * @returns {string} The string to be displayed in the information element. * * @dtopt Callbacks * @name DataTable.defaults.infoCallback * * @example * $('#example').dataTable( { * "infoCallback": function( settings, start, end, max, total, pre ) { * return start +" to "+ end; * } * } ); */ "fnInfoCallback": null, /** * Called when the table has been initialised. Normally DataTables will * initialise sequentially and there will be no need for this function, * however, this does not hold true when using external language information * since that is obtained using an async XHR call. * @type function * @param {object} settings DataTables settings object * @param {object} json The JSON object request from the server - only * present if client-side Ajax sourced data is used * * @dtopt Callbacks * @name DataTable.defaults.initComplete * * @example * $(document).ready( function() { * $('#example').dataTable( { * "initComplete": function(settings, json) { * alert( 'DataTables has finished its initialisation.' ); * } * } ); * } ) */ "fnInitComplete": null, /** * Called at the very start of each table draw and can be used to cancel the * draw by returning false, any other return (including undefined) results in * the full draw occurring). * @type function * @param {object} settings DataTables settings object * @returns {boolean} False will cancel the draw, anything else (including no * return) will allow it to complete. * * @dtopt Callbacks * @name DataTable.defaults.preDrawCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "preDrawCallback": function( settings ) { * if ( $('#test').val() == 1 ) { * return false; * } * } * } ); * } ); */ "fnPreDrawCallback": null, /** * This function allows you to 'post process' each row after it have been * generated for each table draw, but before it is rendered on screen. This * function might be used for setting the row class name etc. * @type function * @param {node} row "TR" element for the current row * @param {array} data Raw data array for this row * @param {int} displayIndex The display index for the current table draw * @param {int} displayIndexFull The index of the data in the full list of * rows (after filtering) * * @dtopt Callbacks * @name DataTable.defaults.rowCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "rowCallback": function( row, data, displayIndex, displayIndexFull ) { * // Bold the grade for all 'A' grade browsers * if ( data[4] == "A" ) { * $('td:eq(4)', row).html( 'A' ); * } * } * } ); * } ); */ "fnRowCallback": null, /** * __Deprecated__ The functionality provided by this parameter has now been * superseded by that provided through `ajax`, which should be used instead. * * This parameter allows you to override the default function which obtains * the data from the server so something more suitable for your application. * For example you could use POST data, or pull information from a Gears or * AIR database. * @type function * @member * @param {string} source HTTP source to obtain the data from (`ajax`) * @param {array} data A key/value pair object containing the data to send * to the server * @param {function} callback to be called on completion of the data get * process that will draw the data on the page. * @param {object} settings DataTables settings object * * @dtopt Callbacks * @dtopt Server-side * @name DataTable.defaults.serverData * * @deprecated 1.10. Please use `ajax` for this functionality now. */ "fnServerData": null, /** * __Deprecated__ The functionality provided by this parameter has now been * superseded by that provided through `ajax`, which should be used instead. * * It is often useful to send extra data to the server when making an Ajax * request - for example custom filtering information, and this callback * function makes it trivial to send extra information to the server. The * passed in parameter is the data set that has been constructed by * DataTables, and you can add to this or modify it as you require. * @type function * @param {array} data Data array (array of objects which are name/value * pairs) that has been constructed by DataTables and will be sent to the * server. In the case of Ajax sourced data with server-side processing * this will be an empty array, for server-side processing there will be a * significant number of parameters! * @returns {undefined} Ensure that you modify the data array passed in, * as this is passed by reference. * * @dtopt Callbacks * @dtopt Server-side * @name DataTable.defaults.serverParams * * @deprecated 1.10. Please use `ajax` for this functionality now. */ "fnServerParams": null, /** * Load the table state. With this function you can define from where, and how, the * state of a table is loaded. By default DataTables will load from `localStorage` * but you might wish to use a server-side database or cookies. * @type function * @member * @param {object} settings DataTables settings object * @return {object} The DataTables state object to be loaded * * @dtopt Callbacks * @name DataTable.defaults.stateLoadCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateLoadCallback": function (settings) { * var o; * * // Send an Ajax request to the server to get the data. Note that * // this is a synchronous request. * $.ajax( { * "url": "/state_load", * "async": false, * "dataType": "json", * "success": function (json) { * o = json; * } * } ); * * return o; * } * } ); * } ); */ "fnStateLoadCallback": function ( settings ) { try { return JSON.parse( localStorage.getItem('DataTables_'+settings.sInstance+'_'+window.location.pathname) ); } catch (e) {} }, /** * Callback which allows modification of the saved state prior to loading that state. * This callback is called when the table is loading state from the stored data, but * prior to the settings object being modified by the saved state. Note that for * plug-in authors, you should use the `stateLoadParams` event to load parameters for * a plug-in. * @type function * @param {object} settings DataTables settings object * @param {object} data The state object that is to be loaded * * @dtopt Callbacks * @name DataTable.defaults.stateLoadParams * * @example * // Remove a saved filter, so filtering is never loaded * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateLoadParams": function (settings, data) { * data.oSearch.sSearch = ""; * } * } ); * } ); * * @example * // Disallow state loading by returning false * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateLoadParams": function (settings, data) { * return false; * } * } ); * } ); */ "fnStateLoadParams": null, /** * Callback that is called when the state has been loaded from the state saving method * and the DataTables settings object has been modified as a result of the loaded state. * @type function * @param {object} settings DataTables settings object * @param {object} data The state object that was loaded * * @dtopt Callbacks * @name DataTable.defaults.stateLoaded * * @example * // Show an alert with the filtering value that was saved * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateLoaded": function (settings, data) { * alert( 'Saved filter was: '+data.oSearch.sSearch ); * } * } ); * } ); */ "fnStateLoaded": null, /** * Save the table state. This function allows you to define where and how the state * information for the table is stored By default DataTables will use `localStorage` * but you might wish to use a server-side database or cookies. * @type function * @member * @param {object} settings DataTables settings object * @param {object} data The state object to be saved * * @dtopt Callbacks * @name DataTable.defaults.stateSaveCallback * * @example * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateSaveCallback": function (settings, data) { * // Send an Ajax request to the server with the state object * $.ajax( { * "url": "/state_save", * "data": data, * "dataType": "json", * "method": "POST" * "success": function () {} * } ); * } * } ); * } ); */ "fnStateSaveCallback": function ( settings, data ) { try { localStorage.setItem( 'DataTables_'+settings.sInstance+'_'+window.location.pathname, JSON.stringify(data) ); } catch (e) {} }, /** * Callback which allows modification of the state to be saved. Called when the table * has changed state a new state save is required. This method allows modification of * the state saving object prior to actually doing the save, including addition or * other state properties or modification. Note that for plug-in authors, you should * use the `stateSaveParams` event to save parameters for a plug-in. * @type function * @param {object} settings DataTables settings object * @param {object} data The state object to be saved * * @dtopt Callbacks * @name DataTable.defaults.stateSaveParams * * @example * // Remove a saved filter, so filtering is never saved * $(document).ready( function() { * $('#example').dataTable( { * "stateSave": true, * "stateSaveParams": function (settings, data) { * data.oSearch.sSearch = ""; * } * } ); * } ); */ "fnStateSaveParams": null, /** * Duration for which the saved state information is considered valid. After this period * has elapsed the state will be returned to the default. * Value is given in seconds. * @type int * @default 7200 (2 hours) * * @dtopt Options * @name DataTable.defaults.stateDuration * * @example * $(document).ready( function() { * $('#example').dataTable( { * "stateDuration": 60*60*24; // 1 day * } ); * } ) */ "iStateDuration": 7200, /** * When enabled DataTables will not make a request to the server for the first * page draw - rather it will use the data already on the page (no sorting etc * will be applied to it), thus saving on an XHR at load time. `deferLoading` * is used to indicate that deferred loading is required, but it is also used * to tell DataTables how many records there are in the full table (allowing * the information element and pagination to be displayed correctly). In the case * where a filtering is applied to the table on initial load, this can be * indicated by giving the parameter as an array, where the first element is * the number of records available after filtering and the second element is the * number of records without filtering (allowing the table information element * to be shown correctly). * @type int | array * @default null * * @dtopt Options * @name DataTable.defaults.deferLoading * * @example * // 57 records available in the table, no filtering applied * $(document).ready( function() { * $('#example').dataTable( { * "serverSide": true, * "ajax": "scripts/server_processing.php", * "deferLoading": 57 * } ); * } ); * * @example * // 57 records after filtering, 100 without filtering (an initial filter applied) * $(document).ready( function() { * $('#example').dataTable( { * "serverSide": true, * "ajax": "scripts/server_processing.php", * "deferLoading": [ 57, 100 ], * "search": { * "search": "my_filter" * } * } ); * } ); */ "iDeferLoading": null, /** * Number of rows to display on a single page when using pagination. If * feature enabled (`lengthChange`) then the end user will be able to override * this to a custom setting using a pop-up menu. * @type int * @default 10 * * @dtopt Options * @name DataTable.defaults.pageLength * * @example * $(document).ready( function() { * $('#example').dataTable( { * "pageLength": 50 * } ); * } ) */ "iDisplayLength": 10, /** * Define the starting point for data display when using DataTables with * pagination. Note that this parameter is the number of records, rather than * the page number, so if you have 10 records per page and want to start on * the third page, it should be "20". * @type int * @default 0 * * @dtopt Options * @name DataTable.defaults.displayStart * * @example * $(document).ready( function() { * $('#example').dataTable( { * "displayStart": 20 * } ); * } ) */ "iDisplayStart": 0, /** * By default DataTables allows keyboard navigation of the table (sorting, paging, * and filtering) by adding a `tabindex` attribute to the required elements. This * allows you to tab through the controls and press the enter key to activate them. * The tabindex is default 0, meaning that the tab follows the flow of the document. * You can overrule this using this parameter if you wish. Use a value of -1 to * disable built-in keyboard navigation. * @type int * @default 0 * * @dtopt Options * @name DataTable.defaults.tabIndex * * @example * $(document).ready( function() { * $('#example').dataTable( { * "tabIndex": 1 * } ); * } ); */ "iTabIndex": 0, /** * Classes that DataTables assigns to the various components and features * that it adds to the HTML table. This allows classes to be configured * during initialisation in addition to through the static * {@link DataTable.ext.oStdClasses} object). * @namespace * @name DataTable.defaults.classes */ "oClasses": {}, /** * All strings that DataTables uses in the user interface that it creates * are defined in this object, allowing you to modified them individually or * completely replace them all as required. * @namespace * @name DataTable.defaults.language */ "oLanguage": { /** * Strings that are used for WAI-ARIA labels and controls only (these are not * actually visible on the page, but will be read by screenreaders, and thus * must be internationalised as well). * @namespace * @name DataTable.defaults.language.aria */ "oAria": { /** * ARIA label that is added to the table headers when the column may be * sorted ascending by activing the column (click or return when focused). * Note that the column header is prefixed to this string. * @type string * @default : activate to sort column ascending * * @dtopt Language * @name DataTable.defaults.language.aria.sortAscending * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "aria": { * "sortAscending": " - click/return to sort ascending" * } * } * } ); * } ); */ "sSortAscending": ":激活排序列升序", /** * ARIA label that is added to the table headers when the column may be * sorted descending by activing the column (click or return when focused). * Note that the column header is prefixed to this string. * @type string * @default : activate to sort column ascending * * @dtopt Language * @name DataTable.defaults.language.aria.sortDescending * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "aria": { * "sortDescending": " - click/return to sort descending" * } * } * } ); * } ); */ "sSortDescending": ":激活排序列降序" }, /** * Pagination string used by DataTables for the built-in pagination * control types. * @namespace * @name DataTable.defaults.language.paginate */ "oPaginate": { /** * Text to use when using the 'full_numbers' type of pagination for the * button to take the user to the first page. * @type string * @default First * * @dtopt Language * @name DataTable.defaults.language.paginate.first * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "paginate": { * "first": "First page" * } * } * } ); * } ); */ "sFirst": "第一页", /** * Text to use when using the 'full_numbers' type of pagination for the * button to take the user to the last page. * @type string * @default Last * * @dtopt Language * @name DataTable.defaults.language.paginate.last * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "paginate": { * "last": "Last page" * } * } * } ); * } ); */ "sLast": "最后一页", /** * Text to use for the 'next' pagination button (to take the user to the * next page). * @type string * @default Next * * @dtopt Language * @name DataTable.defaults.language.paginate.next * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "paginate": { * "next": "Next page" * } * } * } ); * } ); */ "sNext": "下一页", /** * Text to use for the 'previous' pagination button (to take the user to * the previous page). * @type string * @default Previous * * @dtopt Language * @name DataTable.defaults.language.paginate.previous * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "paginate": { * "previous": "Previous page" * } * } * } ); * } ); */ "sPrevious": "上一页" }, /** * This string is shown in preference to `zeroRecords` when the table is * empty of data (regardless of filtering). Note that this is an optional * parameter - if it is not given, the value of `zeroRecords` will be used * instead (either the default or given value). * @type string * @default No data available in table * * @dtopt Language * @name DataTable.defaults.language.emptyTable * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "emptyTable": "No data available in table" * } * } ); * } ); */ "sEmptyTable": "没有数据", /** * This string gives information to the end user about the information * that is current on display on the page. The following tokens can be * used in the string and will be dynamically replaced as the table * display updates. This tokens can be placed anywhere in the string, or * removed as needed by the language requires: * * * `\_START\_` - Display index of the first record on the current page * * `\_END\_` - Display index of the last record on the current page * * `\_TOTAL\_` - Number of records in the table after filtering * * `\_MAX\_` - Number of records in the table without filtering * * `\_PAGE\_` - Current page number * * `\_PAGES\_` - Total number of pages of data in the table * * @type string * @default Showing _START_ to _END_ of _TOTAL_ entries * * @dtopt Language * @name DataTable.defaults.language.info * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "info": "Showing page _PAGE_ of _PAGES_" * } * } ); * } ); */ "sInfo": "显示 _START_ 到 _END_ 项,共 _TOTAL_ 项", /** * Display information string for when the table is empty. Typically the * format of this string should match `info`. * @type string * @default Showing 0 to 0 of 0 entries * * @dtopt Language * @name DataTable.defaults.language.infoEmpty * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "infoEmpty": "No entries to show" * } * } ); * } ); */ "sInfoEmpty": "显示0项", /** * When a user filters the information in a table, this string is appended * to the information (`info`) to give an idea of how strong the filtering * is. The variable _MAX_ is dynamically updated. * @type string * @default (filtered from _MAX_ total entries) * * @dtopt Language * @name DataTable.defaults.language.infoFiltered * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "infoFiltered": " - filtering from _MAX_ records" * } * } ); * } ); */ "sInfoFiltered": "(从 _MAX_ 中筛选)", /** * If can be useful to append extra information to the info string at times, * and this variable does exactly that. This information will be appended to * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are * being used) at all times. * @type string * @default Empty string * * @dtopt Language * @name DataTable.defaults.language.infoPostFix * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "infoPostFix": "All records shown are derived from real information." * } * } ); * } ); */ "sInfoPostFix": "", /** * DataTables has a build in number formatter (`formatNumber`) which is used * to format large numbers that are used in the table information. By * default a comma is used, but this can be trivially changed to any * character you wish with this parameter. * @type string * @default , * * @dtopt Language * @name DataTable.defaults.language.infoThousands * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "infoThousands": "'" * } * } ); * } ); */ "sInfoThousands": ",", /** * Detail the action that will be taken when the drop down menu for the * pagination length option is changed. The '_MENU_' variable is replaced * with a default select list of 10, 25, 50 and 100, and can be replaced * with a custom select box if required. * @type string * @default Show _MENU_ entries * * @dtopt Language * @name DataTable.defaults.language.lengthMenu * * @example * // Language change only * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "lengthMenu": "Display _MENU_ records" * } * } ); * } ); * * @example * // Language and options change * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "lengthMenu": 'Display records' * } * } ); * } ); */ "sLengthMenu": "显示 _MENU_ entries", /** * When using Ajax sourced data and during the first draw when DataTables is * gathering the data, this message is shown in an empty row in the table to * indicate to the end user the the data is being loaded. Note that this * parameter is not used when loading data by server-side processing, just * Ajax sourced data with client-side processing. * @type string * @default Loading... * * @dtopt Language * @name DataTable.defaults.language.loadingRecords * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "loadingRecords": "Please wait - loading..." * } * } ); * } ); */ "sLoadingRecords": "加载中…", /** * Text which is displayed when the table is processing a user action * (usually a sort command or similar). * @type string * @default Processing... * * @dtopt Language * @name DataTable.defaults.language.processing * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "processing": "DataTables is currently busy" * } * } ); * } ); */ "sProcessing": "处理中…", /** * Details the actions that will be taken when the user types into the * filtering input text box. The variable "_INPUT_", if used in the string, * is replaced with the HTML text box for the filtering input allowing * control over where it appears in the string. If "_INPUT_" is not given * then the input box is appended to the string automatically. * @type string * @default Search: * * @dtopt Language * @name DataTable.defaults.language.search * * @example * // Input text box will be appended at the end automatically * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "search": "Filter records:" * } * } ); * } ); * * @example * // Specify where the filter should appear * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "search": "Apply filter _INPUT_ to table" * } * } ); * } ); */ "sSearch": "查找:", /** * All of the language information can be stored in a file on the * server-side, which DataTables will look up if this parameter is passed. * It must store the URL of the language file, which is in a JSON format, * and the object has the same properties as the oLanguage object in the * initialiser object (i.e. the above parameters). Please refer to one of * the example language files to see how this works in action. * @type string * @default Empty string - i.e. disabled * * @dtopt Language * @name DataTable.defaults.language.url * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt" * } * } ); * } ); */ "sUrl": "", /** * Text shown inside the table records when the is no information to be * displayed after filtering. `emptyTable` is shown when there is simply no * information in the table at all (regardless of filtering). * @type string * @default No matching records found * * @dtopt Language * @name DataTable.defaults.language.zeroRecords * * @example * $(document).ready( function() { * $('#example').dataTable( { * "language": { * "zeroRecords": "No records to display" * } * } ); * } ); */ "sZeroRecords": "没有找到符合条件的记录" }, /** * This parameter allows you to have define the global filtering state at * initialisation time. As an object the `search` parameter must be * defined, but all other parameters are optional. When `regex` is true, * the search string will be treated as a regular expression, when false * (default) it will be treated as a straight string. When `smart` * DataTables will use it's smart filtering methods (to word match at * any point in the data), when false this will not be done. * @namespace * @extends DataTable.models.oSearch * * @dtopt Options * @name DataTable.defaults.search * * @example * $(document).ready( function() { * $('#example').dataTable( { * "search": {"search": "Initial search"} * } ); * } ) */ "oSearch": $.extend( {}, DataTable.models.oSearch ), /** * __Deprecated__ The functionality provided by this parameter has now been * superseded by that provided through `ajax`, which should be used instead. * * By default DataTables will look for the property `data` (or `aaData` for * compatibility with DataTables 1.9-) when obtaining data from an Ajax * source or for server-side processing - this parameter allows that * property to be changed. You can use Javascript dotted object notation to * get a data source for multiple levels of nesting. * @type string * @default data * * @dtopt Options * @dtopt Server-side * @name DataTable.defaults.ajaxDataProp * * @deprecated 1.10. Please use `ajax` for this functionality now. */ "sAjaxDataProp": "data", /** * __Deprecated__ The functionality provided by this parameter has now been * superseded by that provided through `ajax`, which should be used instead. * * You can instruct DataTables to load data from an external * source using this parameter (use aData if you want to pass data in you * already have). Simply provide a url a JSON object can be obtained from. * @type string * @default null * * @dtopt Options * @dtopt Server-side * @name DataTable.defaults.ajaxSource * * @deprecated 1.10. Please use `ajax` for this functionality now. */ "sAjaxSource": null, /** * This initialisation variable allows you to specify exactly where in the * DOM you want DataTables to inject the various controls it adds to the page * (for example you might want the pagination controls at the top of the * table). DIV elements (with or without a custom class) can also be added to * aid styling. The follow syntax is used: *
            *
          • The following options are allowed: *
              *
            • 'l' - Length changing
            • *
            • 'f' - Filtering input
            • *
            • 't' - The table!
            • *
            • 'i' - Information
            • *
            • 'p' - Pagination
            • *
            • 'r' - pRocessing
            • *
            *
          • *
          • The following constants are allowed: *
              *
            • 'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')
            • *
            • 'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')
            • *
            *
          • *
          • The following syntax is expected: *
              *
            • '<' and '>' - div elements
            • *
            • '<"class" and '>' - div with a class
            • *
            • '<"#id" and '>' - div with an ID
            • *
            *
          • *
          • Examples: *
              *
            • '<"wrapper"flipt>'
            • *
            • '<lf<t>ip>'
            • *
            *
          • *
          * @type string * @default lfrtip (when `jQueryUI` is false) or * <"H"lfr>t<"F"ip> (when `jQueryUI` is true) * * @dtopt Options * @name DataTable.defaults.dom * * @example * $(document).ready( function() { * $('#example').dataTable( { * "dom": '<"top"i>rt<"bottom"flp><"clear">' * } ); * } ); */ "sDom": "lfrtip", /** * DataTables features four different built-in options for the buttons to * display for pagination control: * * * `simple` - 'Previous' and 'Next' buttons only * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus * page numbers * * Further methods can be added using {@link DataTable.ext.oPagination}. * @type string * @default simple_numbers * * @dtopt Options * @name DataTable.defaults.pagingType * * @example * $(document).ready( function() { * $('#example').dataTable( { * "pagingType": "full_numbers" * } ); * } ) */ "sPaginationType": "simple_numbers", /** * Enable horizontal scrolling. When a table is too wide to fit into a * certain layout, or you have a large number of columns in the table, you * can enable x-scrolling to show the table in a viewport, which can be * scrolled. This property can be `true` which will allow the table to * scroll horizontally when needed, or any CSS unit, or a number (in which * case it will be treated as a pixel measurement). Setting as simply `true` * is recommended. * @type boolean|string * @default blank string - i.e. disabled * * @dtopt Features * @name DataTable.defaults.scrollX * * @example * $(document).ready( function() { * $('#example').dataTable( { * "scrollX": true, * "scrollCollapse": true * } ); * } ); */ "sScrollX": "", /** * This property can be used to force a DataTable to use more width than it * might otherwise do when x-scrolling is enabled. For example if you have a * table which requires to be well spaced, this parameter is useful for * "over-sizing" the table, and thus forcing scrolling. This property can by * any CSS unit, or a number (in which case it will be treated as a pixel * measurement). * @type string * @default blank string - i.e. disabled * * @dtopt Options * @name DataTable.defaults.scrollXInner * * @example * $(document).ready( function() { * $('#example').dataTable( { * "scrollX": "100%", * "scrollXInner": "110%" * } ); * } ); */ "sScrollXInner": "", /** * Enable vertical scrolling. Vertical scrolling will constrain the DataTable * to the given height, and enable scrolling for any data which overflows the * current viewport. This can be used as an alternative to paging to display * a lot of data in a small area (although paging and scrolling can both be * enabled at the same time). This property can be any CSS unit, or a number * (in which case it will be treated as a pixel measurement). * @type string * @default blank string - i.e. disabled * * @dtopt Features * @name DataTable.defaults.scrollY * * @example * $(document).ready( function() { * $('#example').dataTable( { * "scrollY": "200px", * "paginate": false * } ); * } ); */ "sScrollY": "", /** * __Deprecated__ The functionality provided by this parameter has now been * superseded by that provided through `ajax`, which should be used instead. * * Set the HTTP method that is used to make the Ajax call for server-side * processing or Ajax sourced data. * @type string * @default GET * * @dtopt Options * @dtopt Server-side * @name DataTable.defaults.serverMethod * * @deprecated 1.10. Please use `ajax` for this functionality now. */ "sServerMethod": "GET", /** * DataTables makes use of renderers when displaying HTML elements for * a table. These renderers can be added or modified by plug-ins to * generate suitable mark-up for a site. For example the Bootstrap * integration plug-in for DataTables uses a paging button renderer to * display pagination buttons in the mark-up required by Bootstrap. * * For further information about the renderers available see * DataTable.ext.renderer * @type string|object * @default null * * @name DataTable.defaults.renderer * */ "renderer": null }; _fnHungarianMap( DataTable.defaults ); /* * Developer note - See note in model.defaults.js about the use of Hungarian * notation and camel case. */ /** * Column options that can be given to DataTables at initialisation time. * @namespace */ DataTable.defaults.column = { /** * Define which column(s) an order will occur on for this column. This * allows a column's ordering to take multiple columns into account when * doing a sort or use the data from a different column. For example first * name / last name columns make sense to do a multi-column sort over the * two columns. * @type array|int * @default null Takes the value of the column index automatically * * @name DataTable.defaults.column.orderData * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "orderData": [ 0, 1 ], "targets": [ 0 ] }, * { "orderData": [ 1, 0 ], "targets": [ 1 ] }, * { "orderData": 2, "targets": [ 2 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "orderData": [ 0, 1 ] }, * { "orderData": [ 1, 0 ] }, * { "orderData": 2 }, * null, * null * ] * } ); * } ); */ "aDataSort": null, "iDataSort": -1, /** * You can control the default ordering direction, and even alter the * behaviour of the sort handler (i.e. only allow ascending ordering etc) * using this parameter. * @type array * @default [ 'asc', 'desc' ] * * @name DataTable.defaults.column.orderSequence * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "orderSequence": [ "asc" ], "targets": [ 1 ] }, * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] }, * { "orderSequence": [ "desc" ], "targets": [ 3 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * null, * { "orderSequence": [ "asc" ] }, * { "orderSequence": [ "desc", "asc", "asc" ] }, * { "orderSequence": [ "desc" ] }, * null * ] * } ); * } ); */ "asSorting": [ 'asc', 'desc' ], /** * Enable or disable filtering on the data in this column. * @type boolean * @default true * * @name DataTable.defaults.column.searchable * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "searchable": false, "targets": [ 0 ] } * ] } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "searchable": false }, * null, * null, * null, * null * ] } ); * } ); */ "bSearchable": true, /** * Enable or disable ordering on this column. * @type boolean * @default true * * @name DataTable.defaults.column.orderable * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "orderable": false, "targets": [ 0 ] } * ] } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "orderable": false }, * null, * null, * null, * null * ] } ); * } ); */ "bSortable": true, /** * Enable or disable the display of this column. * @type boolean * @default true * * @name DataTable.defaults.column.visible * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "visible": false, "targets": [ 0 ] } * ] } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "visible": false }, * null, * null, * null, * null * ] } ); * } ); */ "bVisible": true, /** * Developer definable function that is called whenever a cell is created (Ajax source, * etc) or processed for input (DOM source). This can be used as a compliment to mRender * allowing you to modify the DOM element (add background colour for example) when the * element is available. * @type function * @param {element} td The TD node that has been created * @param {*} cellData The Data for the cell * @param {array|object} rowData The data for the whole row * @param {int} row The row index for the aoData data store * @param {int} col The column index for aoColumns * * @name DataTable.defaults.column.createdCell * @dtopt Columns * * @example * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [3], * "createdCell": function (td, cellData, rowData, row, col) { * if ( cellData == "1.7" ) { * $(td).css('color', 'blue') * } * } * } ] * }); * } ); */ "fnCreatedCell": null, /** * This parameter has been replaced by `data` in DataTables to ensure naming * consistency. `dataProp` can still be used, as there is backwards * compatibility in DataTables for this option, but it is strongly * recommended that you use `data` in preference to `dataProp`. * @name DataTable.defaults.column.dataProp */ /** * This property can be used to read data from any data source property, * including deeply nested objects / properties. `data` can be given in a * number of different ways which effect its behaviour: * * * `integer` - treated as an array index for the data source. This is the * default that DataTables uses (incrementally increased for each column). * * `string` - read an object property from the data source. There are * three 'special' options that can be used in the string to alter how * DataTables reads the data from the source object: * * `.` - Dotted Javascript notation. Just as you use a `.` in * Javascript to read from nested objects, so to can the options * specified in `data`. For example: `browser.version` or * `browser.name`. If your object parameter name contains a period, use * `\\` to escape it - i.e. `first\\.name`. * * `[]` - Array notation. DataTables can automatically combine data * from and array source, joining the data with the characters provided * between the two brackets. For example: `name[, ]` would provide a * comma-space separated list from the source array. If no characters * are provided between the brackets, the original array source is * returned. * * `()` - Function notation. Adding `()` to the end of a parameter will * execute a function of the name given. For example: `browser()` for a * simple function on the data source, `browser.version()` for a * function in a nested property or even `browser().version` to get an * object property if the function called returns an object. Note that * function notation is recommended for use in `render` rather than * `data` as it is much simpler to use as a renderer. * * `null` - use the original data source for the row rather than plucking * data directly from it. This action has effects on two other * initialisation options: * * `defaultContent` - When null is given as the `data` option and * `defaultContent` is specified for the column, the value defined by * `defaultContent` will be used for the cell. * * `render` - When null is used for the `data` option and the `render` * option is specified for the column, the whole data source for the * row is used for the renderer. * * `function` - the function given will be executed whenever DataTables * needs to set or get the data for a cell in the column. The function * takes three parameters: * * Parameters: * * `{array|object}` The data source for the row * * `{string}` The type call data requested - this will be 'set' when * setting data or 'filter', 'display', 'type', 'sort' or undefined * when gathering data. Note that when `undefined` is given for the * type DataTables expects to get the raw data for the object back< * * `{*}` Data to set when the second parameter is 'set'. * * Return: * * The return value from the function is not required when 'set' is * the type of call, but otherwise the return is what will be used * for the data requested. * * Note that `data` is a getter and setter option. If you just require * formatting of data for output, you will likely want to use `render` which * is simply a getter and thus simpler to use. * * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The * name change reflects the flexibility of this property and is consistent * with the naming of mRender. If 'mDataProp' is given, then it will still * be used by DataTables, as it automatically maps the old name to the new * if required. * * @type string|int|function|null * @default null Use automatically calculated column index * * @name DataTable.defaults.column.data * @dtopt Columns * * @example * // Read table data from objects * // JSON structure for each row: * // { * // "engine": {value}, * // "browser": {value}, * // "platform": {value}, * // "version": {value}, * // "grade": {value} * // } * $(document).ready( function() { * $('#example').dataTable( { * "ajaxSource": "sources/objects.txt", * "columns": [ * { "data": "engine" }, * { "data": "browser" }, * { "data": "platform" }, * { "data": "version" }, * { "data": "grade" } * ] * } ); * } ); * * @example * // Read information from deeply nested objects * // JSON structure for each row: * // { * // "engine": {value}, * // "browser": {value}, * // "platform": { * // "inner": {value} * // }, * // "details": [ * // {value}, {value} * // ] * // } * $(document).ready( function() { * $('#example').dataTable( { * "ajaxSource": "sources/deep.txt", * "columns": [ * { "data": "engine" }, * { "data": "browser" }, * { "data": "platform.inner" }, * { "data": "platform.details.0" }, * { "data": "platform.details.1" } * ] * } ); * } ); * * @example * // Using `data` as a function to provide different information for * // sorting, filtering and display. In this case, currency (price) * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": function ( source, type, val ) { * if (type === 'set') { * source.price = val; * // Store the computed dislay and filter values for efficiency * source.price_display = val=="" ? "" : "$"+numberFormat(val); * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val; * return; * } * else if (type === 'display') { * return source.price_display; * } * else if (type === 'filter') { * return source.price_filter; * } * // 'sort', 'type' and undefined all just use the integer * return source.price; * } * } ] * } ); * } ); * * @example * // Using default content * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": null, * "defaultContent": "Click to edit" * } ] * } ); * } ); * * @example * // Using array notation - outputting a list from an array * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": "name[, ]" * } ] * } ); * } ); * */ "mData": null, /** * This property is the rendering partner to `data` and it is suggested that * when you want to manipulate data for display (including filtering, * sorting etc) without altering the underlying data for the table, use this * property. `render` can be considered to be the the read only companion to * `data` which is read / write (then as such more complex). Like `data` * this option can be given in a number of different ways to effect its * behaviour: * * * `integer` - treated as an array index for the data source. This is the * default that DataTables uses (incrementally increased for each column). * * `string` - read an object property from the data source. There are * three 'special' options that can be used in the string to alter how * DataTables reads the data from the source object: * * `.` - Dotted Javascript notation. Just as you use a `.` in * Javascript to read from nested objects, so to can the options * specified in `data`. For example: `browser.version` or * `browser.name`. If your object parameter name contains a period, use * `\\` to escape it - i.e. `first\\.name`. * * `[]` - Array notation. DataTables can automatically combine data * from and array source, joining the data with the characters provided * between the two brackets. For example: `name[, ]` would provide a * comma-space separated list from the source array. If no characters * are provided between the brackets, the original array source is * returned. * * `()` - Function notation. Adding `()` to the end of a parameter will * execute a function of the name given. For example: `browser()` for a * simple function on the data source, `browser.version()` for a * function in a nested property or even `browser().version` to get an * object property if the function called returns an object. * * `object` - use different data for the different data types requested by * DataTables ('filter', 'display', 'type' or 'sort'). The property names * of the object is the data type the property refers to and the value can * defined using an integer, string or function using the same rules as * `render` normally does. Note that an `_` option _must_ be specified. * This is the default value to use if you haven't specified a value for * the data type requested by DataTables. * * `function` - the function given will be executed whenever DataTables * needs to set or get the data for a cell in the column. The function * takes three parameters: * * Parameters: * * {array|object} The data source for the row (based on `data`) * * {string} The type call data requested - this will be 'filter', * 'display', 'type' or 'sort'. * * {array|object} The full data source for the row (not based on * `data`) * * Return: * * The return value from the function is what will be used for the * data requested. * * @type string|int|function|object|null * @default null Use the data source value. * * @name DataTable.defaults.column.render * @dtopt Columns * * @example * // Create a comma separated list from an array of objects * $(document).ready( function() { * $('#example').dataTable( { * "ajaxSource": "sources/deep.txt", * "columns": [ * { "data": "engine" }, * { "data": "browser" }, * { * "data": "platform", * "render": "[, ].name" * } * ] * } ); * } ); * * @example * // Execute a function to obtain data * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": null, // Use the full data source object for the renderer's source * "render": "browserName()" * } ] * } ); * } ); * * @example * // As an object, extracting different data for the different types * // This would be used with a data source such as: * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" } * // Here the `phone` integer is used for sorting and type detection, while `phone_filter` * // (which has both forms) is used for filtering for if a user inputs either format, while * // the formatted phone number is the one that is shown in the table. * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": null, // Use the full data source object for the renderer's source * "render": { * "_": "phone", * "filter": "phone_filter", * "display": "phone_display" * } * } ] * } ); * } ); * * @example * // Use as a function to create a link from the data source * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "data": "download_link", * "render": function ( data, type, full ) { * return 'Download'; * } * } ] * } ); * } ); */ "mRender": null, /** * Change the cell type created for the column - either TD cells or TH cells. This * can be useful as TH cells have semantic meaning in the table body, allowing them * to act as a header for a row (you may wish to add scope='row' to the TH elements). * @type string * @default td * * @name DataTable.defaults.column.cellType * @dtopt Columns * * @example * // Make the first column use TH cells * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ { * "targets": [ 0 ], * "cellType": "th" * } ] * } ); * } ); */ "sCellType": "td", /** * Class to give to each cell in this column. * @type string * @default Empty string * * @name DataTable.defaults.column.class * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "class": "my_class", "targets": [ 0 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "class": "my_class" }, * null, * null, * null, * null * ] * } ); * } ); */ "sClass": "", /** * When DataTables calculates the column widths to assign to each column, * it finds the longest string in each column and then constructs a * temporary table and reads the widths from that. The problem with this * is that "mmm" is much wider then "iiii", but the latter is a longer * string - thus the calculation can go wrong (doing it properly and putting * it into an DOM object and measuring that is horribly(!) slow). Thus as * a "work around" we provide this option. It will append its value to the * text that is found to be the longest string for the column - i.e. padding. * Generally you shouldn't need this! * @type string * @default Empty string * * @name DataTable.defaults.column.contentPadding * @dtopt Columns * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * null, * null, * null, * { * "contentPadding": "mmm" * } * ] * } ); * } ); */ "sContentPadding": "", /** * Allows a default value to be given for a column's data, and will be used * whenever a null data source is encountered (this can be because `data` * is set to null, or because the data source itself is null). * @type string * @default null * * @name DataTable.defaults.column.defaultContent * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { * "data": null, * "defaultContent": "Edit", * "targets": [ -1 ] * } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * null, * null, * null, * { * "data": null, * "defaultContent": "Edit" * } * ] * } ); * } ); */ "sDefaultContent": null, /** * This parameter is only used in DataTables' server-side processing. It can * be exceptionally useful to know what columns are being displayed on the * client side, and to map these to database fields. When defined, the names * also allow DataTables to reorder information from the server if it comes * back in an unexpected order (i.e. if you switch your columns around on the * client-side, your server-side code does not also need updating). * @type string * @default Empty string * * @name DataTable.defaults.column.name * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "name": "engine", "targets": [ 0 ] }, * { "name": "browser", "targets": [ 1 ] }, * { "name": "platform", "targets": [ 2 ] }, * { "name": "version", "targets": [ 3 ] }, * { "name": "grade", "targets": [ 4 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "name": "engine" }, * { "name": "browser" }, * { "name": "platform" }, * { "name": "version" }, * { "name": "grade" } * ] * } ); * } ); */ "sName": "", /** * Defines a data source type for the ordering which can be used to read * real-time information from the table (updating the internally cached * version) prior to ordering. This allows ordering to occur on user * editable elements such as form inputs. * @type string * @default std * * @name DataTable.defaults.column.orderDataType * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] }, * { "type": "numeric", "targets": [ 3 ] }, * { "orderDataType": "dom-select", "targets": [ 4 ] }, * { "orderDataType": "dom-checkbox", "targets": [ 5 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * null, * null, * { "orderDataType": "dom-text" }, * { "orderDataType": "dom-text", "type": "numeric" }, * { "orderDataType": "dom-select" }, * { "orderDataType": "dom-checkbox" } * ] * } ); * } ); */ "sSortDataType": "std", /** * The title of this column. * @type string * @default null Derived from the 'TH' value for this column in the * original HTML table. * * @name DataTable.defaults.column.title * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "title": "My column title", "targets": [ 0 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "title": "My column title" }, * null, * null, * null, * null * ] * } ); * } ); */ "sTitle": null, /** * The type allows you to specify how the data for this column will be * ordered. Four types (string, numeric, date and html (which will strip * HTML tags before ordering)) are currently available. Note that only date * formats understood by Javascript's Date() object will be accepted as type * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', * 'numeric', 'date' or 'html' (by default). Further types can be adding * through plug-ins. * @type string * @default null Auto-detected from raw data * * @name DataTable.defaults.column.type * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "type": "html", "targets": [ 0 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "type": "html" }, * null, * null, * null, * null * ] * } ); * } ); */ "sType": null, /** * Defining the width of the column, this parameter may take any CSS value * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not * been given a specific width through this interface ensuring that the table * remains readable. * @type string * @default null Automatic * * @name DataTable.defaults.column.width * @dtopt Columns * * @example * // Using `columnDefs` * $(document).ready( function() { * $('#example').dataTable( { * "columnDefs": [ * { "width": "20%", "targets": [ 0 ] } * ] * } ); * } ); * * @example * // Using `columns` * $(document).ready( function() { * $('#example').dataTable( { * "columns": [ * { "width": "20%" }, * null, * null, * null, * null * ] * } ); * } ); */ "sWidth": null }; _fnHungarianMap( DataTable.defaults.column ); /** * DataTables settings object - this holds all the information needed for a * given table, including configuration, data and current application of the * table options. DataTables does not have a single instance for each DataTable * with the settings attached to that instance, but rather instances of the * DataTable "class" are created on-the-fly as needed (typically by a * $().dataTable() call) and the settings object is then applied to that * instance. * * Note that this object is related to {@link DataTable.defaults} but this * one is the internal data store for DataTables's cache of columns. It should * NOT be manipulated outside of DataTables. Any configuration should be done * through the initialisation options. * @namespace * @todo Really should attach the settings object to individual instances so we * don't need to create new instances on each $().dataTable() call (if the * table already exists). It would also save passing oSettings around and * into every single function. However, this is a very significant * architecture change for DataTables and will almost certainly break * backwards compatibility with older installations. This is something that * will be done in 2.0. */ DataTable.models.oSettings = { /** * Primary features of DataTables and their enablement state. * @namespace */ "oFeatures": { /** * Flag to say if DataTables should automatically try to calculate the * optimum table and columns widths (true) or not (false). * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bAutoWidth": null, /** * Delay the creation of TR and TD elements until they are actually * needed by a driven page draw. This can give a significant speed * increase for Ajax source and Javascript source data, but makes no * difference at all fro DOM and server-side processing tables. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bDeferRender": null, /** * Enable filtering on the table or not. Note that if this is disabled * then there is no filtering at all on the table, including fnFilter. * To just remove the filtering input use sDom and remove the 'f' option. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bFilter": null, /** * Table information element (the 'Showing x of y records' div) enable * flag. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bInfo": null, /** * Present a user control allowing the end user to change the page size * when pagination is enabled. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bLengthChange": null, /** * Pagination enabled or not. Note that if this is disabled then length * changing must also be disabled. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bPaginate": null, /** * Processing indicator enable flag whenever DataTables is enacting a * user request - typically an Ajax request for server-side processing. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bProcessing": null, /** * Server-side processing enabled flag - when enabled DataTables will * get all data from the server for every draw - there is no filtering, * sorting or paging done on the client-side. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bServerSide": null, /** * Sorting enablement flag. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bSort": null, /** * Multi-column sorting * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bSortMulti": null, /** * Apply a class to the columns which are being sorted to provide a * visual highlight or not. This can slow things down when enabled since * there is a lot of DOM interaction. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bSortClasses": null, /** * State saving enablement flag. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bStateSave": null }, /** * Scrolling settings for a table. * @namespace */ "oScroll": { /** * When the table is shorter in height than sScrollY, collapse the * table container down to the height of the table (when true). * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bCollapse": null, /** * Width of the scrollbar for the web-browser's platform. Calculated * during table initialisation. * @type int * @default 0 */ "iBarWidth": 0, /** * Viewport width for horizontal scrolling. Horizontal scrolling is * disabled if an empty string. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string */ "sX": null, /** * Width to expand the table to when using x-scrolling. Typically you * should not need to use this. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string * @deprecated */ "sXInner": null, /** * Viewport height for vertical scrolling. Vertical scrolling is disabled * if an empty string. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string */ "sY": null }, /** * Language information for the table. * @namespace * @extends DataTable.defaults.oLanguage */ "oLanguage": { /** * Information callback function. See * {@link DataTable.defaults.fnInfoCallback} * @type function * @default null */ "fnInfoCallback": null }, /** * Browser support parameters * @namespace */ "oBrowser": { /** * Indicate if the browser incorrectly calculates width:100% inside a * scrolling element (IE6/7) * @type boolean * @default false */ "bScrollOversize": false, /** * Determine if the vertical scrollbar is on the right or left of the * scrolling container - needed for rtl language layout, although not * all browsers move the scrollbar (Safari). * @type boolean * @default false */ "bScrollbarLeft": false }, "ajax": null, /** * Array referencing the nodes which are used for the features. The * parameters of this object match what is allowed by sDom - i.e. *
            *
          • 'l' - Length changing
          • *
          • 'f' - Filtering input
          • *
          • 't' - The table!
          • *
          • 'i' - Information
          • *
          • 'p' - Pagination
          • *
          • 'r' - pRocessing
          • *
          * @type array * @default [] */ "aanFeatures": [], /** * Store data information - see {@link DataTable.models.oRow} for detailed * information. * @type array * @default [] */ "aoData": [], /** * Array of indexes which are in the current display (after filtering etc) * @type array * @default [] */ "aiDisplay": [], /** * Array of indexes for display - no filtering * @type array * @default [] */ "aiDisplayMaster": [], /** * Store information about each column that is in use * @type array * @default [] */ "aoColumns": [], /** * Store information about the table's header * @type array * @default [] */ "aoHeader": [], /** * Store information about the table's footer * @type array * @default [] */ "aoFooter": [], /** * Store the applied global search information in case we want to force a * research or compare the old search to a new one. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @namespace * @extends DataTable.models.oSearch */ "oPreviousSearch": {}, /** * Store the applied search for each column - see * {@link DataTable.models.oSearch} for the format that is used for the * filtering information for each column. * @type array * @default [] */ "aoPreSearchCols": [], /** * Sorting that is applied to the table. Note that the inner arrays are * used in the following manner: *
            *
          • Index 0 - column number
          • *
          • Index 1 - current sorting direction
          • *
          * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type array * @todo These inner arrays should really be objects */ "aaSorting": null, /** * Sorting that is always applied to the table (i.e. prefixed in front of * aaSorting). * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type array * @default [] */ "aaSortingFixed": [], /** * Classes to use for the striping of a table. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type array * @default [] */ "asStripeClasses": null, /** * If restoring a table - we should restore its striping classes as well * @type array * @default [] */ "asDestroyStripes": [], /** * If restoring a table - we should restore its width * @type int * @default 0 */ "sDestroyWidth": 0, /** * Callback functions array for every time a row is inserted (i.e. on a draw). * @type array * @default [] */ "aoRowCallback": [], /** * Callback functions for the header on each draw. * @type array * @default [] */ "aoHeaderCallback": [], /** * Callback function for the footer on each draw. * @type array * @default [] */ "aoFooterCallback": [], /** * Array of callback functions for draw callback functions * @type array * @default [] */ "aoDrawCallback": [], /** * Array of callback functions for row created function * @type array * @default [] */ "aoRowCreatedCallback": [], /** * Callback functions for just before the table is redrawn. A return of * false will be used to cancel the draw. * @type array * @default [] */ "aoPreDrawCallback": [], /** * Callback functions for when the table has been initialised. * @type array * @default [] */ "aoInitComplete": [], /** * Callbacks for modifying the settings to be stored for state saving, prior to * saving state. * @type array * @default [] */ "aoStateSaveParams": [], /** * Callbacks for modifying the settings that have been stored for state saving * prior to using the stored values to restore the state. * @type array * @default [] */ "aoStateLoadParams": [], /** * Callbacks for operating on the settings object once the saved state has been * loaded * @type array * @default [] */ "aoStateLoaded": [], /** * Cache the table ID for quick access * @type string * @default Empty string */ "sTableId": "", /** * The TABLE node for the main table * @type node * @default null */ "nTable": null, /** * Permanent ref to the thead element * @type node * @default null */ "nTHead": null, /** * Permanent ref to the tfoot element - if it exists * @type node * @default null */ "nTFoot": null, /** * Permanent ref to the tbody element * @type node * @default null */ "nTBody": null, /** * Cache the wrapper node (contains all DataTables controlled elements) * @type node * @default null */ "nTableWrapper": null, /** * Indicate if when using server-side processing the loading of data * should be deferred until the second draw. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean * @default false */ "bDeferLoading": false, /** * Indicate if all required information has been read in * @type boolean * @default false */ "bInitialised": false, /** * Information about open rows. Each object in the array has the parameters * 'nTr' and 'nParent' * @type array * @default [] */ "aoOpenRows": [], /** * Dictate the positioning of DataTables' control elements - see * {@link DataTable.model.oInit.sDom}. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string * @default null */ "sDom": null, /** * Which type of pagination should be used. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string * @default two_button */ "sPaginationType": "two_button", /** * The state duration (for `stateSave`) in seconds. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type int * @default 0 */ "iStateDuration": 0, /** * Array of callback functions for state saving. Each array element is an * object with the following parameters: *
            *
          • function:fn - function to call. Takes two parameters, oSettings * and the JSON string to save that has been thus far created. Returns * a JSON string to be inserted into a json object * (i.e. '"param": [ 0, 1, 2]')
          • *
          • string:sName - name of callback
          • *
          * @type array * @default [] */ "aoStateSave": [], /** * Array of callback functions for state loading. Each array element is an * object with the following parameters: *
            *
          • function:fn - function to call. Takes two parameters, oSettings * and the object stored. May return false to cancel state loading
          • *
          • string:sName - name of callback
          • *
          * @type array * @default [] */ "aoStateLoad": [], /** * State that was loaded. Useful for back reference * @type object * @default null */ "oLoadedState": null, /** * Source url for AJAX data for the table. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string * @default null */ "sAjaxSource": null, /** * Property from a given object from which to read the table data from. This * can be an empty string (when not server-side processing), in which case * it is assumed an an array is given directly. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string */ "sAjaxDataProp": null, /** * Note if draw should be blocked while getting data * @type boolean * @default true */ "bAjaxDataGet": true, /** * The last jQuery XHR object that was used for server-side data gathering. * This can be used for working with the XHR information in one of the * callbacks * @type object * @default null */ "jqXHR": null, /** * JSON returned from the server in the last Ajax request * @type object * @default undefined */ "json": undefined, /** * Function to get the server-side data. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type function */ "fnServerData": null, /** * Functions which are called prior to sending an Ajax request so extra * parameters can easily be sent to the server * @type array * @default [] */ "aoServerParams": [], /** * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if * required). * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type string */ "sServerMethod": null, /** * Format numbers for display. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type function */ "fnFormatNumber": null, /** * List of options that can be used for the user selectable length menu. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type array * @default [] */ "aLengthMenu": null, /** * Counter for the draws that the table does. Also used as a tracker for * server-side processing * @type int * @default 0 */ "iDraw": 0, /** * Indicate if a redraw is being done - useful for Ajax * @type boolean * @default false */ "bDrawing": false, /** * Draw index (iDraw) of the last error when parsing the returned data * @type int * @default -1 */ "iDrawError": -1, /** * Paging display length * @type int * @default 10 */ "_iDisplayLength": 10, /** * Paging start point - aiDisplay index * @type int * @default 0 */ "_iDisplayStart": 0, /** * Server-side processing - number of records in the result set * (i.e. before filtering), Use fnRecordsTotal rather than * this property to get the value of the number of records, regardless of * the server-side processing setting. * @type int * @default 0 * @private */ "_iRecordsTotal": 0, /** * Server-side processing - number of records in the current display set * (i.e. after filtering). Use fnRecordsDisplay rather than * this property to get the value of the number of records, regardless of * the server-side processing setting. * @type boolean * @default 0 * @private */ "_iRecordsDisplay": 0, /** * Flag to indicate if jQuery UI marking and classes should be used. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bJUI": null, /** * The classes to use for the table * @type object * @default {} */ "oClasses": {}, /** * Flag attached to the settings object so you can check in the draw * callback if filtering has been done in the draw. Deprecated in favour of * events. * @type boolean * @default false * @deprecated */ "bFiltered": false, /** * Flag attached to the settings object so you can check in the draw * callback if sorting has been done in the draw. Deprecated in favour of * events. * @type boolean * @default false * @deprecated */ "bSorted": false, /** * Indicate that if multiple rows are in the header and there is more than * one unique cell per column, if the top one (true) or bottom one (false) * should be used for sorting / title by DataTables. * Note that this parameter will be set by the initialisation routine. To * set a default use {@link DataTable.defaults}. * @type boolean */ "bSortCellsTop": null, /** * Initialisation object that is used for the table * @type object * @default null */ "oInit": null, /** * Destroy callback functions - for plug-ins to attach themselves to the * destroy so they can clean up markup and events. * @type array * @default [] */ "aoDestroyCallback": [], /** * Get the number of records in the current record set, before filtering * @type function */ "fnRecordsTotal": function () { return _fnDataSource( this ) == 'ssp' ? this._iRecordsTotal * 1 : this.aiDisplayMaster.length; }, /** * Get the number of records in the current record set, after filtering * @type function */ "fnRecordsDisplay": function () { return _fnDataSource( this ) == 'ssp' ? this._iRecordsDisplay * 1 : this.aiDisplay.length; }, /** * Get the display end point - aiDisplay index * @type function */ "fnDisplayEnd": function () { var len = this._iDisplayLength, start = this._iDisplayStart, calc = start + len, records = this.aiDisplay.length, features = this.oFeatures, paginate = features.bPaginate; if ( features.bServerSide ) { return paginate === false || len === -1 ? start + records : Math.min( start+len, this._iRecordsDisplay ); } else { return ! paginate || calc>records || len===-1 ? records : calc; } }, /** * The DataTables object for this table * @type object * @default null */ "oInstance": null, /** * Unique identifier for each instance of the DataTables object. If there * is an ID on the table node, then it takes that value, otherwise an * incrementing internal counter is used. * @type string * @default null */ "sInstance": null, /** * tabindex attribute value that is added to DataTables control elements, allowing * keyboard navigation of the table and its controls. */ "iTabIndex": 0, /** * DIV container for the footer scrolling table if scrolling */ "nScrollHead": null, /** * DIV container for the footer scrolling table if scrolling */ "nScrollFoot": null, /** * Last applied sort * @type array * @default [] */ "aLastSort": [], /** * Stored plug-in instances * @type object * @default {} */ "oPlugins": {} }; /** * Extension object for DataTables that is used to provide all extension * options. * * Note that the `DataTable.ext` object is available through * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is * also aliased to `jQuery.fn.dataTableExt` for historic reasons. * @namespace * @extends DataTable.models.ext */ /** * DataTables extensions * * This namespace acts as a collection area for plug-ins that can be used to * extend DataTables capabilities. Indeed many of the build in methods * use this method to provide their own capabilities (sorting methods for * example). * * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy * reasons * * @namespace */ DataTable.ext = _ext = { /** * Element class names * * @type object * @default {} */ classes: {}, /** * Error reporting. * * How should DataTables report an error. Can take the value 'alert' or * 'throw' * * @type string * @default alert */ errMode: "alert", /** * Feature plug-ins. * * This is an array of objects which describe the feature plug-ins that are * available to DataTables. These feature plug-ins are then available for * use through the `dom` initialisation option. * * Each feature plug-in is described by an object which must have the * following properties: * * * `fnInit` - function that is used to initialise the plug-in, * * `cFeature` - a character so the feature can be enabled by the `dom` * instillation option. This is case sensitive. * * The `fnInit` function has the following input parameters: * * 1. `{object}` DataTables settings object: see * {@link DataTable.models.oSettings} * * And the following return is expected: * * * {node|null} The element which contains your feature. Note that the * return may also be void if your plug-in does not require to inject any * DOM elements into DataTables control (`dom`) - for example this might * be useful when developing a plug-in which allows table control via * keyboard entry * * @type array * * @example * $.fn.dataTable.ext.features.push( { * "fnInit": function( oSettings ) { * return new TableTools( { "oDTSettings": oSettings } ); * }, * "cFeature": "T" * } ); */ feature: [], /** * Row searching. * * This method of searching is complimentary to the default type based * searching, and a lot more comprehensive as it allows you complete control * over the searching logic. Each element in this array is a function * (parameters described below) that is called for every row in the table, * and your logic decides if it should be included in the searching data set * or not. * * Searching functions have the following input parameters: * * 1. `{object}` DataTables settings object: see * {@link DataTable.models.oSettings} * 2. `{array|object}` Data for the row to be processed (same as the * original format that was passed in as the data source, or an array * from a DOM data source * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which * can be useful to retrieve the `TR` element if you need DOM interaction. * * And the following return is expected: * * * {boolean} Include the row in the searched result set (true) or not * (false) * * Note that as with the main search ability in DataTables, technically this * is "filtering", since it is subtractive. However, for consistency in * naming we call it searching here. * * @type array * @default [] * * @example * // The following example shows custom search being applied to the * // fourth column (i.e. the data[3] index) based on two input values * // from the end-user, matching the data in a certain range. * $.fn.dataTable.ext.search.push( * function( settings, data, dataIndex ) { * var min = document.getElementById('min').value * 1; * var max = document.getElementById('max').value * 1; * var version = data[3] == "-" ? 0 : data[3]*1; * * if ( min == "" && max == "" ) { * return true; * } * else if ( min == "" && version < max ) { * return true; * } * else if ( min < version && "" == max ) { * return true; * } * else if ( min < version && version < max ) { * return true; * } * return false; * } * ); */ search: [], /** * Internal functions, exposed for used in plug-ins. * * Please note that you should not need to use the internal methods for * anything other than a plug-in (and even then, try to avoid if possible). * The internal function may change between releases. * * @type object * @default {} */ internal: {}, /** * Legacy configuration options. Enable and disable legacy options that * are available in DataTables. * * @type object */ legacy: { /** * Enable / disable DataTables 1.9 compatible server-side processing * requests * * @type boolean * @default false */ ajax: false }, /** * Pagination plug-in methods. * * Each entry in this object is a function and defines which buttons should * be shown by the pagination rendering method that is used for the table: * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the * buttons are displayed in the document, while the functions here tell it * what buttons to display. This is done by returning an array of button * descriptions (what each button will do). * * Pagination types (the four built in options and any additional plug-in * options defined here) can be used through the `paginationType` * initialisation parameter. * * The functions defined take two parameters: * * 1. `{int} page` The current page index * 2. `{int} pages` The number of pages in the table * * Each function is expected to return an array where each element of the * array can be one of: * * * `first` - Jump to first page when activated * * `last` - Jump to last page when activated * * `previous` - Show previous page when activated * * `next` - Show next page when activated * * `{int}` - Show page of the index given * * `{array}` - A nested array containing the above elements to add a * containing 'DIV' element (might be useful for styling). * * Note that DataTables v1.9- used this object slightly differently whereby * an object with two functions would be defined for each plug-in. That * ability is still supported by DataTables 1.10+ to provide backwards * compatibility, but this option of use is now decremented and no longer * documented in DataTables 1.10+. * * @type object * @default {} * * @example * // Show previous, next and current page buttons only * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { * return [ 'previous', page, 'next' ]; * }; */ pager: {}, renderer: { pageButton: {}, header: {} }, /** * Ordering plug-ins - custom data source * * The extension options for ordering of data available here is complimentary * to the default type based ordering that DataTables typically uses. It * allows much greater control over the the data that is being used to * order a column, but is necessarily therefore more complex. * * This type of ordering is useful if you want to do ordering based on data * live from the DOM (for example the contents of an 'input' element) rather * than just the static string that DataTables knows of. * * The way these plug-ins work is that you create an array of the values you * wish to be ordering for the column in question and then return that * array. The data in the array much be in the index order of the rows in * the table (not the currently ordering order!). Which order data gathering * function is run here depends on the `dt-init columns.orderDataType` * parameter that is used for the column (if any). * * The functions defined take two parameters: * * 1. `{object}` DataTables settings object: see * {@link DataTable.models.oSettings} * 2. `{int}` Target column index * * Each function is expected to return an array: * * * `{array}` Data for the column to be ordering upon * * @type array * * @example * // Ordering using `input` node values * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) * { * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { * return $('input', td).val(); * } ); * } */ order: {}, /** * Type based plug-ins. * * Each column in DataTables has a type assigned to it, either by automatic * detection or by direct assignment using the `type` option for the column. * The type of a column will effect how it is ordering and search (plug-ins * can also make use of the column type if required). * * @namespace */ type: { /** * Type detection functions. * * The functions defined in this object are used to automatically detect * a column's type, making initialisation of DataTables super easy, even * when complex data is in the table. * * The functions defined take a single parameter: * * 1. `{*}` Data from the column cell to be analysed * * Each function is expected to return: * * * `{string|null}` Data type detected, or null if unknown (and thus * pass it on to the other type detection functions. * * @type array * * @example * // Currency type detection plug-in: * $.fn.dataTable.ext.type.detect.push( * function ( data ) { * // Check the numeric part * if ( ! $.isNumeric( data.substring(1) ) ) { * return null; * } * * // Check prefixed by currency * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { * return 'currency'; * } * return null; * } * ); */ detect: [], /** * Type based search formatting. * * The type based searching functions can be used to pre-format the * data to be search on. For example, it can be used to strip HTML * tags or to de-format telephone numbers for numeric only searching. * * Note that is a search is not defined for a column of a given type, * no search formatting will be performed. * * Pre-processing of searching data plug-ins - When you assign the sType * for a column (or have it automatically detected for you by DataTables * or a type detection plug-in), you will typically be using this for * custom sorting, but it can also be used to provide custom searching * by allowing you to pre-processing the data and returning the data in * the format that should be searched upon. This is done by adding * functions this object with a parameter name which matches the sType * for that target column. This is the corollary of afnSortData * for searching data. * * The functions defined take a single parameter: * * 1. `{*}` Data from the column cell to be prepared for searching * * Each function is expected to return: * * * `{string|null}` Formatted string that will be used for the searching. * * @type object * @default {} * * @example * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); * } */ search: {}, /** * Type based ordering. * * The column type tells DataTables what ordering to apply to the table * when a column is sorted upon. The order for each type that is defined, * is defined by the functions available in this object. * * Each ordering option can be described by three properties added to * this object: * * * `{type}-pre` - Pre-formatting function * * `{type}-asc` - Ascending order function * * `{type}-desc` - Descending order function * * All three can be used together, only `{type}-pre` or only * `{type}-asc` and `{type}-desc` together. It is generally recommended * that only `{type}-pre` is used, as this provides the optimal * implementation in terms of speed, although the others are provided * for compatibility with existing Javascript sort functions. * * `{type}-pre`: Functions defined take a single parameter: * * 1. `{*}` Data from the column cell to be prepared for ordering * * And return: * * * `{*}` Data to be sorted upon * * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort * functions, taking two parameters: * * 1. `{*}` Data to compare to the second parameter * 2. `{*}` Data to compare to the first parameter * * And returning: * * * `{*}` Ordering match: <0 if first parameter should be sorted lower * than the second parameter, ===0 if the two parameters are equal and * >0 if the first parameter should be sorted height than the second * parameter. * * @type object * @default {} * * @example * // Numeric ordering of formatted numbers with a pre-formatter * $.extend( $.fn.dataTable.ext.type.order, { * "string-pre": function(x) { * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); * return parseFloat( a ); * } * } ); * * @example * // Case-sensitive string ordering, with no pre-formatting method * $.extend( $.fn.dataTable.ext.order, { * "string-case-asc": function(x,y) { * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); * }, * "string-case-desc": function(x,y) { * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); * } * } ); */ order: {} }, /** * Unique DataTables instance counter * * @type int * @private */ _unique: 0, // // Depreciated // The following properties are retained for backwards compatiblity only. // The should not be used in new projects and will be removed in a future // version // /** * Version check function. * @type function * @depreciated Since 1.10 */ fnVersionCheck: DataTable.fnVersionCheck, /** * Index for what 'this' index API functions should use * @type int * @deprecated Since v1.10 */ iApiIndex: 0, /** * jQuery UI class container * @type object * @deprecated Since v1.10 */ oJUIClasses: {}, /** * Software version * @type string * @deprecated Since v1.10 */ sVersion: DataTable.version }; // // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts // $.extend( _ext, { afnFiltering: _ext.filter, aTypes: _ext.type.detect, ofnSearch: _ext.type.search, oSort: _ext.type.order, afnSortData: _ext.order, aoFeatures: _ext.feature, oApi: _ext.internal, oStdClasses: _ext.classes, oPagination: _ext.pager } ); $.extend( DataTable.ext.classes, { "sTable": "dataTable", "sNoFooter": "no-footer", /* Paging buttons */ "sPageButton": "paginate_button", "sPageButtonActive": "current", "sPageButtonDisabled": "disabled", /* Striping classes */ "sStripeOdd": "odd", "sStripeEven": "even", /* Empty row */ "sRowEmpty": "dataTables_empty", /* Features */ "sWrapper": "dataTables_wrapper", "sFilter": "dataTables_filter", "sInfo": "dataTables_info", "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ "sLength": "dataTables_length", "sProcessing": "dataTables_processing", /* Sorting */ "sSortAsc": "sorting_asc", "sSortDesc": "sorting_desc", "sSortable": "sorting", /* Sortable in both directions */ "sSortableAsc": "sorting_asc_disabled", "sSortableDesc": "sorting_desc_disabled", "sSortableNone": "sorting_disabled", "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ /* Filtering */ "sFilterInput": "", /* Page length */ "sLengthSelect": "", /* Scrolling */ "sScrollWrapper": "dataTables_scroll", "sScrollHead": "dataTables_scrollHead", "sScrollHeadInner": "dataTables_scrollHeadInner", "sScrollBody": "dataTables_scrollBody", "sScrollFoot": "dataTables_scrollFoot", "sScrollFootInner": "dataTables_scrollFootInner", /* Misc */ "sHeaderTH": "", "sFooterTH": "", // Deprecated "sSortJUIAsc": "", "sSortJUIDesc": "", "sSortJUI": "", "sSortJUIAscAllowed": "", "sSortJUIDescAllowed": "", "sSortJUIWrapper": "", "sSortIcon": "", "sJUIHeader": "", "sJUIFooter": "" } ); (function() { // Reused strings for better compression. Closure compiler appears to have a // weird edge case where it is trying to expand strings rather than use the // variable version. This results in about 200 bytes being added, for very // little preference benefit since it this run on script load only. var _empty = ''; _empty = ''; var _stateDefault = _empty + 'ui-state-default'; var _sortIcon = _empty + 'css_right ui-icon ui-icon-'; var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix'; $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, { /* Full numbers paging buttons */ "sPageButton": "fg-button ui-button "+_stateDefault, "sPageButtonActive": "ui-state-disabled", "sPageButtonDisabled": "ui-state-disabled", /* Features */ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ "ui-buttonset-multi paging_", /* Note that the type is postfixed */ /* Sorting */ "sSortAsc": _stateDefault+" sorting_asc", "sSortDesc": _stateDefault+" sorting_desc", "sSortable": _stateDefault+" sorting", "sSortableAsc": _stateDefault+" sorting_asc_disabled", "sSortableDesc": _stateDefault+" sorting_desc_disabled", "sSortableNone": _stateDefault+" sorting_disabled", "sSortJUIAsc": _sortIcon+"triangle-1-n", "sSortJUIDesc": _sortIcon+"triangle-1-s", "sSortJUI": _sortIcon+"carat-2-n-s", "sSortJUIAscAllowed": _sortIcon+"carat-1-n", "sSortJUIDescAllowed": _sortIcon+"carat-1-s", "sSortJUIWrapper": "DataTables_sort_wrapper", "sSortIcon": "DataTables_sort_icon", /* Scrolling */ "sScrollHead": "dataTables_scrollHead "+_stateDefault, "sScrollFoot": "dataTables_scrollFoot "+_stateDefault, /* Misc */ "sHeaderTH": _stateDefault, "sFooterTH": _stateDefault, "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr", "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br" } ); }()); var extPagination = DataTable.ext.pager; function _numbers ( page, pages ) { var numbers = [], buttons = extPagination.numbers_length, half = Math.floor( buttons / 2 ), i = 1; if ( pages <= buttons ) { numbers = _range( 0, pages ); } else if ( page <= half ) { numbers = _range( 0, buttons-2 ); numbers.push( 'ellipsis' ); numbers.push( pages-1 ); } else if ( page >= pages - 1 - half ) { numbers = _range( pages-(buttons-2), pages ); numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6 numbers.splice( 0, 0, 0 ); } else { numbers = _range( page-1, page+2 ); numbers.push( 'ellipsis' ); numbers.push( pages-1 ); numbers.splice( 0, 0, 'ellipsis' ); numbers.splice( 0, 0, 0 ); } numbers.DT_el = 'span'; return numbers; } $.extend( extPagination, { simple: function ( page, pages ) { return [ 'previous', 'next' ]; }, full: function ( page, pages ) { return [ 'first', 'previous', 'next', 'last' ]; }, simple_numbers: function ( page, pages ) { return [ 'previous', _numbers(page, pages), 'next' ]; }, full_numbers: function ( page, pages ) { return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ]; }, // For testing and plug-ins to use _numbers: _numbers, numbers_length: 7 } ); $.extend( true, DataTable.ext.renderer, { pageButton: { _: function ( settings, host, idx, buttons, page, pages ) { var classes = settings.oClasses; var lang = settings.oLanguage.oPaginate; var btnDisplay, btnClass; var attach = function( container, buttons ) { var i, ien, node, button; var clickHandler = function ( e ) { _fnPageChange( settings, e.data.action, true ); }; for ( i=0, ien=buttons.length ; i' ) .appendTo( container ); attach( inner, button ); } else { btnDisplay = ''; btnClass = ''; switch ( button ) { case 'ellipsis': container.append(''); break; case 'first': btnDisplay = lang.sFirst; btnClass = button + (page > 0 ? '' : ' '+classes.sPageButtonDisabled); break; case 'previous': btnDisplay = lang.sPrevious; btnClass = button + (page > 0 ? '' : ' '+classes.sPageButtonDisabled); break; case 'next': btnDisplay = lang.sNext; btnClass = button + (page < pages-1 ? '' : ' '+classes.sPageButtonDisabled); break; case 'last': btnDisplay = lang.sLast; btnClass = button + (page < pages-1 ? '' : ' '+classes.sPageButtonDisabled); break; default: btnDisplay = button + 1; btnClass = page === button ? classes.sPageButtonActive : ''; break; } if ( btnDisplay ) { node = $('', { 'class': classes.sPageButton+' '+btnClass, 'aria-controls': settings.sTableId, 'tabindex': settings.iTabIndex, 'id': idx === 0 && typeof button === 'string' ? settings.sTableId +'_'+ button : null } ) .html( btnDisplay ) .appendTo( container ); _fnBindAction( node, {action: button}, clickHandler ); } } } }; attach( $(host).empty(), buttons ); } } } ); var __numericReplace = function ( d, re1, re2 ) { if ( !d || d === '-' ) { return -Infinity; } if ( d.replace ) { if ( re1 ) { d = d.replace( re1, '' ); } if ( re2 ) { d = d.replace( re2, '' ); } } return d * 1; }; $.extend( DataTable.ext.type.order, { // Dates "date-pre": function ( d ) { return Date.parse( d ) || 0; }, // Plain numbers "numeric-pre": function ( d ) { return __numericReplace( d ); }, // Formatted numbers "numeric-fmt-pre": function ( d ) { return __numericReplace( d, _re_formatted_numeric ); }, // HTML numeric "html-numeric-pre": function ( d ) { return __numericReplace( d, _re_html ); }, // HTML numeric, formatted "html-numeric-fmt-pre": function ( d ) { return __numericReplace( d, _re_html, _re_formatted_numeric ); }, // html "html-pre": function ( a ) { return a.replace ? a.replace( /<.*?>/g, "" ).toLowerCase() : a+''; }, // string "string-pre": function ( a ) { return typeof a === 'string' ? a.toLowerCase() : ! a || ! a.toString ? '' : a.toString(); }, // string-asc and -desc are retained only for compatibility with the old // sort methods "string-asc": function ( x, y ) { return ((x < y) ? -1 : ((x > y) ? 1 : 0)); }, "string-desc": function ( x, y ) { return ((x < y) ? 1 : ((x > y) ? -1 : 0)); } } ); // Built in type detection. See model.ext.aTypes for information about // what is required from this methods. $.extend( DataTable.ext.type.detect, [ // Plain numbers - first since V8 detects some plain numbers as dates // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...). function ( d ) { return _isNumber( d ) ? 'numeric' : null; }, // Dates (only those recognised by the browser's Date.parse) function ( d ) { // V8 will remove any unknown characters at the start of the expression, // leading to false matches such as `$245.12` being a valid date. See // forum thread 18941 for detail. if ( d && ! _re_date_start.test(d) ) { return null; } var parsed = Date.parse(d); return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; }, // Formatted numbers function ( d ) { return _isNumber( d, true ) ? 'numeric-fmt' : null; }, // HTML numeric function ( d ) { return _htmlNumeric( d ) ? 'html-numeric' : null; }, // HTML numeric, formatted function ( d ) { return _htmlNumeric( d, true ) ? 'html-numeric-fmt' : null; }, // HTML (this is strict checking - there much be html) function ( d ) { return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 'html' : null; } ] ); // Filter formatting functions. See model.ext.ofnSearch for information about // what is required from these methods. $.extend( DataTable.ext.type.search, { html: function ( data ) { return _empty(data) ? '' : typeof data === 'string' ? data .replace( _re_new_lines, " " ) .replace( _re_html, "" ) : ''; }, string: function ( data ) { return _empty(data) ? '' : typeof data === 'string' ? data.replace( _re_new_lines, " " ) : data; } } ); $.extend( true, DataTable.ext.renderer, { header: { _: function ( settings, cell, column, idx, classes ) { // No additional mark-up required // Attach a sort listener to update on sort $(settings.nTable).on( 'order.dt', function ( e, settings, sorting, columns ) { cell .removeClass( column.sSortingClass +' '+ classes.sSortAsc +' '+ classes.sSortDesc ) .addClass( columns[ idx ] == 'asc' ? classes.sSortAsc : columns[ idx ] == 'desc' ? classes.sSortDesc : column.sSortingClass ); } ); }, jqueryui: function ( settings, cell, column, idx, classes ) { $('
          ') .addClass( classes.sSortJUIWrapper ) .append( cell.contents() ) .append( $('') .addClass( classes.sSortIcon+' '+column.sSortingClassJUI ) ) .appendTo( cell ); // Attach a sort listener to update on sort $(settings.nTable).on( 'order.dt', function ( e, settings, sorting, columns ) { cell .removeClass( classes.sSortAsc +" "+classes.sSortDesc ) .addClass( columns[ idx ] == 'asc' ? classes.sSortAsc : columns[ idx ] == 'desc' ? classes.sSortDesc : column.sSortingClass ); cell .find( 'span' ) .removeClass( classes.sSortJUIAsc +" "+ classes.sSortJUIDesc +" "+ classes.sSortJUI +" "+ classes.sSortJUIAscAllowed +" "+ classes.sSortJUIDescAllowed ) .addClass( columns[ idx ] == 'asc' ? classes.sSortJUIAsc : columns[ idx ] == 'desc' ? classes.sSortJUIDesc : column.sSortingClassJUI ); } ); } } } ); // jQuery access $.fn.dataTable = DataTable; // Legacy aliases $.fn.dataTableSettings = DataTable.settings; $.fn.dataTableExt = DataTable.ext; // With a capital `D` we return a DataTables API instance rather than a // jQuery object $.fn.DataTable = function ( opts ) { return $(this).dataTable( opts ).api(); }; // All properties that are available to $.fn.dataTable should also be // available on $.fn.DataTable $.each( DataTable, function ( prop, val ) { $.fn.DataTable[ prop ] = val; } ); // Information about events fired by DataTables - for documentation. /** * Draw event, fired whenever the table is redrawn on the page, at the same * point as fnDrawCallback. This may be useful for binding events or * performing calculations when the table is altered at all. * @name DataTable#draw.dt * @event * @param {event} e jQuery event object * @param {object} o DataTables settings object {@link DataTable.models.oSettings} */ /** * Search event, fired when the searching applied to the table (using the * built-in global search, or column filters) is altered. * @name DataTable#search.dt * @event * @param {event} e jQuery event object * @param {object} o DataTables settings object {@link DataTable.models.oSettings} */ /** * Page change event, fired when the paging of the table is altered. * @name DataTable#page.dt * @event * @param {event} e jQuery event object * @param {object} o DataTables settings object {@link DataTable.models.oSettings} */ /** * Order event, fired when the ordering applied to the table is altered. * @name DataTable#order.dt * @event * @param {event} e jQuery event object * @param {object} o DataTables settings object {@link DataTable.models.oSettings} */ /** * DataTables initialisation complete event, fired when the table is fully * drawn, including Ajax data loaded, if Ajax data is required. * @name DataTable#init.dt * @event * @param {event} e jQuery event object * @param {object} oSettings DataTables settings object * @param {object} json The JSON object request from the server - only * present if client-side Ajax sourced data is used */ /** * State save event, fired when the table has changed state a new state save * is required. This event allows modification of the state saving object * prior to actually doing the save, including addition or other state * properties (for plug-ins) or modification of a DataTables core property. * @name DataTable#stateSaveParams.dt * @event * @param {event} e jQuery event object * @param {object} oSettings DataTables settings object * @param {object} json The state information to be saved */ /** * State load event, fired when the table is loading state from the stored * data, but prior to the settings object being modified by the saved state * - allowing modification of the saved state is required or loading of * state for a plug-in. * @name DataTable#stateLoadParams.dt * @event * @param {event} e jQuery event object * @param {object} oSettings DataTables settings object * @param {object} json The saved state information */ /** * State loaded event, fired when state has been loaded from stored data and * the settings object has been modified by the loaded data. * @name DataTable#stateLoaded.dt * @event * @param {event} e jQuery event object * @param {object} oSettings DataTables settings object * @param {object} json The saved state information */ /** * Processing event, fired when DataTables is doing some kind of processing * (be it, order, searcg or anything else). It can be used to indicate to * the end user that there is something happening, or that something has * finished. * @name DataTable#processing.dt * @event * @param {event} e jQuery event object * @param {object} oSettings DataTables settings object * @param {boolean} bShow Flag for if DataTables is doing processing or not */ /** * Ajax (XHR) event, fired whenever an Ajax request is completed from a * request to made to the server for new data. This event is called before * DataTables processed the returned data, so it can also be used to pre- * process the data returned from the server, if needed. * * Note that this trigger is called in `fnServerData`, if you override * `fnServerData` and which to use this event, you need to trigger it in you * success function. * @name DataTable#xhr.dt * @event * @param {event} e jQuery event object * @param {object} o DataTables settings object {@link DataTable.models.oSettings} * @param {object} json JSON returned from the server * * @example * // Use a custom property returned from the server in another DOM element * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { * $('#status').html( json.status ); * } ); * * @example * // Pre-process the data returned from the server * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { * for ( var i=0, ien=json.aaData.length ; i
          "); target.before(_main_div); _main_div.append(target); target.addClass("table table-hover treegrid-table"); if (options.striped) { target.addClass('table-striped'); } // 工具条在外层包装一下div,样式用的bootstrap-table的 if(options.toolbar){ var _tool_div = $("
          "); var _tool_left_div = $("
          "); _tool_left_div.append($(options.toolbar)); _tool_div.append(_tool_left_div); _main_div.before(_tool_div); } // 得到根节点 target.getRootNodes = function(data) { // 指定Root节点值 var _root = options.rootCodeValue?options.rootCodeValue:null var result = []; $.each(data, function(index, item) { // 这里兼容几种常见Root节点写法 // 默认的几种判断 var _defaultRootFlag = item[options.parentCode] == '0' || item[options.parentCode] == 0 || item[options.parentCode] == null || item[options.parentCode] == ''; if (!item[options.parentCode] || (_root?(item[options.parentCode] == options.rootCodeValue):_defaultRootFlag)){ result.push(item); } // 添加一个默认属性,用来判断当前节点有没有被显示 item.isShow = false; }); return result; }; var j = 0; // 递归获取子节点并且设置子节点 target.getChildNodes = function(data, parentNode, parentIndex, tbody) { $.each(data, function(i, item) { if (item[options.parentCode] == parentNode[options.code]) { var tr = $('
          '); var nowParentIndex = (parentIndex + (j++) + 1); tr.addClass('treegrid-' + nowParentIndex); tr.addClass('treegrid-parent-' + parentIndex); target.renderRow(tr,item); item.isShow = true; tbody.append(tr); target.getChildNodes(data, item, nowParentIndex, tbody) } }); }; // 绘制行 target.renderRow = function(tr,item){ $.each(options.columns, function(index, column) { // 判断有没有选择列 if(index==0&&column.field=='selectItem'){ hasSelectItem = false; var td = $(''); if(column.radio){ var _ipt = $(''); td.append(_ipt); } if(column.checkbox){ var _ipt = $(''); td.append(_ipt); } tr.append(td); }else{ var td = $(''); // 增加formatter渲染 if (column.formatter) { td.html(column.formatter.call(this, '', item, index)); } else { td.text(item[column.field]); } tr.append(td); } }); } // 加载数据 target.load = function(parms){ // 加载数据前先清空 target.html(""); // 构造表头 var thr = $(''); $.each(options.columns, function(i, item) { var th = null; // 判断有没有选择列 if(i==0&&item.field=='selectItem'){ hasSelectItem = true; th = $(''); }else{ th = $(''); } th.text(item.title); thr.append(th); }); var thead = $(''); thead.append(thr); target.append(thead); // 构造表体 var tbody = $(''); target.append(tbody); // 添加加载loading var _loading = '' tbody.html(_loading); // 默认高度 if(options.height){ tbody.css("height",options.height); } $.ajax({ type : options.type, url : options.url, data : parms?parms:options.ajaxParams, dataType : "JSON", success : function(data, textStatus, jqXHR) { // 加载完数据先清空 tbody.html(""); if(!data||data.length<=0){ var _empty = '' tbody.html(_empty); return; } var rootNode = target.getRootNodes(data); $.each(rootNode, function(i, item) { var tr = $(''); tr.addClass('treegrid-' + (j + "_" + i)); target.renderRow(tr,item); item.isShow = true; tbody.append(tr); target.getChildNodes(data, item, (j + "_" + i), tbody); }); // 下边的操作主要是为了查询时让一些没有根节点的节点显示 $.each(data, function(i, item) { if(!item.isShow){ var tr = $(''); tr.addClass('treegrid-' + (j + "_" + i)); target.renderRow(tr,item); tbody.append(tr); } }); target.append(tbody); // 初始化treegrid target.treegrid({ treeColumn: options.expandColumn?options.expandColumn:(hasSelectItem?1:0),//如果有radio或checkbox默认第二列层级显示,当前是在用户未设置的提前下 expanderExpandedClass : options.expanderExpandedClass, expanderCollapsedClass : options.expanderCollapsedClass }); if (!options.expandAll) { target.treegrid('collapseAll'); } //动态设置表头宽度 thead.css("width", tbody.children(":first").css("width")); // 行点击选中事件 target.find("tbody").find("tr").click(function(){ if(hasSelectItem){ var _ipt = $(this).find("input[name='select_item']"); if(_ipt.attr("type")=="radio"){ _ipt.prop('checked',true); target.find("tbody").find("tr").removeClass("treegrid-selected"); $(this).addClass("treegrid-selected"); }else{ if(_ipt.prop('checked')){ _ipt.prop('checked',false); $(this).removeClass("treegrid-selected"); }else{ _ipt.prop('checked',true); $(this).addClass("treegrid-selected"); } } } }); }, error:function(xhr,textStatus){ var _errorMsg = '' tbody.html(_errorMsg); debugger; }, }); } if (options.url) { target.load(); } else { // 也可以通过defaults里面的data属性通过传递一个数据集合进来对组件进行初始化....有兴趣可以自己实现,思路和上述类似 } return target; }; // 组件方法封装........ $.fn.bootstrapTreeTable.methods = { // 返回选中记录的id(返回的id由配置中的id属性指定) // 为了兼容bootstrap-table的写法,统一返回数组,这里只返回了指定的id getSelections : function(target, data) { // 所有被选中的记录input var _ipt = target.find("tbody").find("tr").find("input[name='select_item']:checked"); var chk_value =[]; // 如果是radio if(_ipt.attr("type")=="radio"){ chk_value.push({id:_ipt.val()}); }else{ _ipt.each(function(_i,_item){ chk_value.push({id:$(_item).val()}); }); } return chk_value; }, // 刷新记录 refresh : function(target, parms) { if(parms){ target.load(parms); }else{ target.load(); } }, // 组件的其他方法也可以进行类似封装........ }; $.fn.bootstrapTreeTable.defaults = { id : 'id',// 选取记录返回的值 code : 'code',// 用于设置父子关系 parentCode : 'parentId',// 用于设置父子关系 rootCodeValue: null,//设置根节点code值----可指定根节点,默认为null,"",0,"0" data : [], // 构造table的数据集合 type : "GET", // 请求数据的ajax类型 url : null, // 请求数据的ajax的url ajaxParams : {}, // 请求数据的ajax的data属性 expandColumn : null,// 在哪一列上面显示展开按钮 expandAll : true, // 是否全部展开 striped : false, // 是否各行渐变色 columns : [], toolbar: null,//顶部工具条 height: 0, expanderExpandedClass : 'glyphicon glyphicon-chevron-down',// 展开的按钮的图标 expanderCollapsedClass : 'glyphicon glyphicon-chevron-right'// 缩起的按钮的图标 }; })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/jquery-treegrid/js/jquery.treegrid.bootstrap3.js ================================================ $.extend($.fn.treegrid.defaults, { expanderExpandedClass: 'glyphicon glyphicon-chevron-down', expanderCollapsedClass: 'glyphicon glyphicon-chevron-right' }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/extend/layer.ext.js ================================================ /*! layer弹层组件拓展类 */ ;!function(){layer.use("skin/layer.ext.css",function(){layer.layui_layer_extendlayerextjs=!0});var a=layer.cache||{},b=function(b){return a.skin?" "+a.skin+" "+a.skin+"-"+b:""};layer.prompt=function(a,c){a=a||{},"function"==typeof a&&(c=a);var d,e=2==a.formType?'":function(){return''}();return layer.open($.extend({btn:["确定","取消"],content:e,skin:"layui-layer-prompt"+b("prompt"),success:function(a){d=a.find(".layui-layer-input"),d.focus()},yes:function(b){var e=d.val();""===e?d.focus():e.length>(a.maxlength||500)?layer.tips("最多输入"+(a.maxlength||500)+"个字数",d,{tips:1}):c&&c(e,b,d)}},a))},layer.tab=function(a){a=a||{};var c=a.tab||{};return layer.open($.extend({type:1,skin:"layui-layer-tab"+b("tab"),title:function(){var a=c.length,b=1,d="";if(a>0)for(d=''+c[0].title+"";a>b;b++)d+=""+c[b].title+"";return d}(),content:'
            '+function(){var a=c.length,b=1,d="";if(a>0)for(d='
          • '+(c[0].content||"no content")+"
          • ";a>b;b++)d+='
          • '+(c[b].content||"no content")+"
          • ";return d}()+"
          ",success:function(a){var b=a.find(".layui-layer-title").children(),c=a.find(".layui-layer-tabmain").children();b.on("mousedown",function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0;var b=$(this),d=b.index();b.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),c.eq(d).show().siblings().hide()})}},a))},layer.photos=function(a,c,d){function e(a,b,c){var d=new Image;d.onload=function(){d.onload=null,b(d)},d.onerror=function(a){d.onerror=null,c(a)},d.src=a}var f={};if(a=a||{},a.photos){var g=a.photos.constructor===Object,h=g?a.photos:{},i=h.data||[],j=h.start||0;if(f.imgIndex=j+1,g){if(0===i.length)return void layer.msg("没有图片")}else{var k=$(a.photos),l=k.find(a.img||"img");if(0===l.length)return;if(c||k.find(h.img||"img").each(function(b){var c=$(this);i.push({alt:c.attr("alt"),pid:c.attr("layer-pid"),src:c.attr("layer-src")||c.attr("src"),thumb:c.attr("src")}),c.on("click",function(){layer.photos($.extend(a,{photos:{start:b,data:i,tab:a.tab},full:a.full}),!0)})}),!c)return}f.imgprev=function(a){f.imgIndex--,f.imgIndex<1&&(f.imgIndex=i.length),f.tabimg(a)},f.imgnext=function(a,b){f.imgIndex++,f.imgIndex>i.length&&(f.imgIndex=1,b)||f.tabimg(a)},f.keyup=function(a){if(!f.end){var b=a.keyCode;a.preventDefault(),37===b?f.imgprev(!0):39===b?f.imgnext(!0):27===b&&layer.close(f.index)}},f.tabimg=function(b){i.length<=1||(h.start=f.imgIndex-1,layer.close(f.index),layer.photos(a,!0,b))},f.event=function(){f.bigimg.hover(function(){f.imgsee.show()},function(){f.imgsee.hide()}),f.bigimg.find(".layui-layer-imgprev").on("click",function(a){a.preventDefault(),f.imgprev()}),f.bigimg.find(".layui-layer-imgnext").on("click",function(a){a.preventDefault(),f.imgnext()}),$(document).on("keyup",f.keyup)},f.loadi=layer.load(1,{shade:"shade"in a?!1:.9,scrollbar:!1}),e(i[j].src,function(c){layer.close(f.loadi),f.index=layer.open($.extend({type:1,area:function(){var b=[c.width,c.height],d=[$(window).width()-100,$(window).height()-100];return!a.full&&b[0]>d[0]&&(b[0]=d[0],b[1]=b[0]*d[1]/b[0]),[b[0]+"px",b[1]+"px"]}(),title:!1,shade:.9,shadeClose:!0,closeBtn:!1,move:".layui-layer-phimg img",moveType:1,scrollbar:!1,moveOut:!0,shift:5*Math.random()|0,skin:"layui-layer-photos"+b("photos"),content:'
          '+(i[j].alt||
          '+(i.length>1?'':"")+'
          '+(i[j].alt||"")+""+f.imgIndex+"/"+i.length+"
          ",success:function(b,c){f.bigimg=b.find(".layui-layer-phimg"),f.imgsee=b.find(".layui-layer-imguide,.layui-layer-imgbar"),f.event(b),a.tab&&a.tab(i[j],b)},end:function(){f.end=!0,$(document).off("keyup",f.keyup)}},a))},function(){layer.close(f.loadi),layer.msg("当前图片地址异常
          是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){i.length>1&&f.imgnext(!0,!0)}})})}}}(); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/laydate/laydate.js ================================================ /** @Name : layDate v1.1 日期控件 @Author: 贤心 @Date: 2014-06-25 @QQ群:176047195 @Site:http://sentsin.com/layui/laydate */ ;!function(a){var b={path:"",defSkin:"default",format:"YYYY-MM-DD",min:"1900-01-01 00:00:00",max:"2099-12-31 23:59:59",isv:!1},c={},d=document,e="createElement",f="getElementById",g="getElementsByTagName",h=["laydate_box","laydate_void","laydate_click","LayDateSkin","skins/","/laydate.css"];a.laydate=function(b){b=b||{};try{h.event=a.event?a.event:laydate.caller.arguments[0]}catch(d){}return c.run(b),laydate},laydate.v="1.1",c.getPath=function(){var a=document.scripts,c=a[a.length-1].src;return b.path?b.path:c.substring(0,c.lastIndexOf("/")+1)}(),c.use=function(a,b){var f=d[e]("link");f.type="text/css",f.rel="stylesheet",f.href=c.getPath+a+h[5],b&&(f.id=b),d[g]("head")[0].appendChild(f),f=null},c.trim=function(a){return a=a||"",a.replace(/^\s|\s$/g,"").replace(/\s+/g," ")},c.digit=function(a){return 10>a?"0"+(0|a):a},c.stopmp=function(b){return b=b||a.event,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0,this},c.each=function(a,b){for(var c=0,d=a.length;d>c&&b(c,a[c])!==!1;c++);},c.hasClass=function(a,b){return a=a||{},new RegExp("\\b"+b+"\\b").test(a.className)},c.addClass=function(a,b){return a=a||{},c.hasClass(a,b)||(a.className+=" "+b),a.className=c.trim(a.className),this},c.removeClass=function(a,b){if(a=a||{},c.hasClass(a,b)){var d=new RegExp("\\b"+b+"\\b");a.className=a.className.replace(d,"")}return this},c.removeCssAttr=function(a,b){var c=a.style;c.removeProperty?c.removeProperty(b):c.removeAttribute(b)},c.shde=function(a,b){a.style.display=b?"none":"block"},c.query=function(a){var e,b,h,i,j;return a=c.trim(a).split(" "),b=d[f](a[0].substr(1)),b?a[1]?/^\./.test(a[1])?(i=a[1].substr(1),j=new RegExp("\\b"+i+"\\b"),e=[],h=d.getElementsByClassName?b.getElementsByClassName(i):b[g]("*"),c.each(h,function(a,b){j.test(b.className)&&e.push(b)}),e[0]?e:""):(e=b[g](a[1]),e[0]?b[g](a[1]):""):b:void 0},c.on=function(b,d,e){return b.attachEvent?b.attachEvent("on"+d,function(){e.call(b,a.even)}):b.addEventListener(d,e,!1),c},c.stopMosup=function(a,b){"mouseup"!==a&&c.on(b,"mouseup",function(a){c.stopmp(a)})},c.run=function(a){var d,e,g,b=c.query,f=h.event;try{g=f.target||f.srcElement||{}}catch(i){g={}}if(d=a.elem?b(a.elem):g,f&&g.tagName){if(!d||d===c.elem)return;c.stopMosup(f.type,d),c.stopmp(f),c.view(d,a),c.reshow()}else e=a.event||"click",c.each((0|d.length)>0?d:[d],function(b,d){c.stopMosup(e,d),c.on(d,e,function(b){c.stopmp(b),d!==c.elem&&(c.view(d,a),c.reshow())})})},c.scroll=function(a){return a=a?"scrollLeft":"scrollTop",d.body[a]|d.documentElement[a]},c.winarea=function(a){return document.documentElement[a?"clientWidth":"clientHeight"]},c.isleap=function(a){return 0===a%4&&0!==a%100||0===a%400},c.checkVoid=function(a,b,d){var e=[];return a=0|a,b=0|b,d=0|d,ac.maxs[0]?e=["y",1]:a>=c.mins[0]&&a<=c.maxs[0]&&(a==c.mins[0]&&(bc.maxs[1]?e=["m",1]:b==c.maxs[1]&&d>c.maxs[2]&&(e=["d",1]))),e},c.timeVoid=function(a,b){if(c.ymd[1]+1==c.mins[1]&&c.ymd[2]==c.mins[2]){if(0===b&&ac.maxs[3])return 1;if(1===b&&a>c.maxs[4])return 1;if(2===b&&a>c.maxs[5])return 1}return a>(b?59:23)?1:void 0},c.check=function(){var a=c.options.format.replace(/YYYY|MM|DD|hh|mm|ss/g,"\\d+\\").replace(/\\$/g,""),b=new RegExp(a),d=c.elem[h.elemv],e=d.match(/\d+/g)||[],f=c.checkVoid(e[0],e[1],e[2]);if(""!==d.replace(/\s/g,"")){if(!b.test(d))return c.elem[h.elemv]="",c.msg("日期不符合格式,请重新选择。"),1;if(f[0])return c.elem[h.elemv]="",c.msg("日期不在有效期内,请重新选择。"),1;f.value=c.elem[h.elemv].match(b).join(),e=f.value.match(/\d+/g),e[1]<1?(e[1]=1,f.auto=1):e[1]>12?(e[1]=12,f.auto=1):e[1].length<2&&(f.auto=1),e[2]<1?(e[2]=1,f.auto=1):e[2]>c.months[(0|e[1])-1]?(e[2]=31,f.auto=1):e[2].length<2&&(f.auto=1),e.length>3&&(c.timeVoid(e[3],0)&&(f.auto=1),c.timeVoid(e[4],1)&&(f.auto=1),c.timeVoid(e[5],2)&&(f.auto=1)),f.auto?c.creation([e[0],0|e[1],0|e[2]],1):f.value!==c.elem[h.elemv]&&(c.elem[h.elemv]=f.value)}},c.months=[31,null,31,30,31,30,31,31,30,31,30,31],c.viewDate=function(a,b,d){var f=(c.query,{}),g=new Date;a<(0|c.mins[0])&&(a=0|c.mins[0]),a>(0|c.maxs[0])&&(a=0|c.maxs[0]),g.setFullYear(a,b,d),f.ymd=[g.getFullYear(),g.getMonth(),g.getDate()],c.months[1]=c.isleap(f.ymd[0])?29:28,g.setFullYear(f.ymd[0],f.ymd[1],1),f.FDay=g.getDay(),f.PDay=c.months[0===b?11:b-1]-f.FDay+1,f.NDay=1,c.each(h.tds,function(a,b){var g,d=f.ymd[0],e=f.ymd[1]+1;b.className="",a=f.FDay&&a'+a+"年":'
        • '+(a-7+b)+"年
        • "}),b("#laydate_ys").innerHTML=d,c.each(b("#laydate_ys li"),function(a,b){"y"===c.checkVoid(b.getAttribute("y"))[0]?c.addClass(b,h[1]):c.on(b,"click",function(a){c.stopmp(a).reshow(),c.viewDate(0|this.getAttribute("y"),c.ymd[1],c.ymd[2])})})},c.initDate=function(){var d=(c.query,new Date),e=c.elem[h.elemv].match(/\d+/g)||[];e.length<3&&(e=c.options.start.match(/\d+/g)||[],e.length<3&&(e=[d.getFullYear(),d.getMonth()+1,d.getDate()])),c.inymd=e,c.viewDate(e[0],e[1]-1,e[2])},c.iswrite=function(){var a=c.query,b={time:a("#laydate_hms")};c.shde(b.time,!c.options.istime),c.shde(h.oclear,!("isclear"in c.options?c.options.isclear:1)),c.shde(h.otoday,!("istoday"in c.options?c.options.istoday:1)),c.shde(h.ok,!("issure"in c.options?c.options.issure:1))},c.orien=function(a,b){var d,e=c.elem.getBoundingClientRect();a.style.left=e.left+(b?0:c.scroll(1))+"px",d=e.bottom+a.offsetHeight/1.5<=c.winarea()?e.bottom-1:e.top>a.offsetHeight/1.5?e.top-a.offsetHeight+1:c.winarea()-a.offsetHeight,a.style.top=d+(b?0:c.scroll())+"px"},c.follow=function(a){c.options.fixed?(a.style.position="fixed",c.orien(a,1)):(a.style.position="absolute",c.orien(a))},c.viewtb=function(){var a,b=[],f=["日","一","二","三","四","五","六"],h={},i=d[e]("table"),j=d[e]("thead");return j.appendChild(d[e]("tr")),h.creath=function(a){var b=d[e]("th");b.innerHTML=f[a],j[g]("tr")[0].appendChild(b),b=null},c.each(new Array(6),function(d){b.push([]),a=i.insertRow(0),c.each(new Array(7),function(c){b[d][c]=0,0===d&&h.creath(c),a.insertCell(c)})}),i.insertBefore(j,i.children[0]),i.id=i.className="laydate_table",a=b=null,i.outerHTML.toLowerCase()}(),c.view=function(a,f){var i,g=c.query,j={};f=f||a,c.elem=a,c.options=f,c.options.format||(c.options.format=b.format),c.options.start=c.options.start||"",c.mm=j.mm=[c.options.min||b.min,c.options.max||b.max],c.mins=j.mm[0].match(/\d+/g),c.maxs=j.mm[1].match(/\d+/g),h.elemv=/textarea|input/.test(c.elem.tagName.toLocaleLowerCase())?"value":"innerHTML",c.box?c.shde(c.box):(i=d[e]("div"),i.id=h[0],i.className=h[0],i.style.cssText="position: absolute;",i.setAttribute("name","laydate-v"+laydate.v),i.innerHTML=j.html='
            '+function(){var a="";return c.each(new Array(12),function(b){a+=''+c.digit(b+1)+"月"}),a}()+"
            "+"
            "+"
            "+c.viewtb+'
            '+'
              '+'
            • 时间
            • '+"
            • :
            • "+"
            • :
            • "+"
            • "+"
            "+'
            '+'
            '+'清空'+'今天'+'确认'+"
            "+(b.isv?'laydate-v'+laydate.v+"":"")+"
            ",d.body.appendChild(i),c.box=g("#"+h[0]),c.events(),i=null),c.follow(c.box),f.zIndex?c.box.style.zIndex=f.zIndex:c.removeCssAttr(c.box,"z-index"),c.stopMosup("click",c.box),c.initDate(),c.iswrite(),c.check()},c.reshow=function(){return c.each(c.query("#"+h[0]+" .laydate_show"),function(a,b){c.removeClass(b,"laydate_show")}),this},c.close=function(){c.reshow(),c.shde(c.query("#"+h[0]),1),c.elem=null},c.parse=function(a,d,e){return a=a.concat(d),e=e||(c.options?c.options.format:b.format),e.replace(/YYYY|MM|DD|hh|mm|ss/g,function(){return a.index=0|++a.index,c.digit(a[a.index])})},c.creation=function(a,b){var e=(c.query,c.hmsin),f=c.parse(a,[e[0].value,e[1].value,e[2].value]);c.elem[h.elemv]=f,b||(c.close(),"function"==typeof c.options.choose&&c.options.choose(f))},c.events=function(){var b=c.query,e={box:"#"+h[0]};c.addClass(d.body,"laydate_body"),h.tds=b("#laydate_table td"),h.mms=b("#laydate_ms span"),h.year=b("#laydate_y"),h.month=b("#laydate_m"),c.each(b(e.box+" .laydate_ym"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.addClass(this[g]("div")[0],"laydate_show"),a||(e.YY=parseInt(h.year.value),c.viewYears(e.YY))})}),c.on(b(e.box),"click",function(){c.reshow()}),e.tabYear=function(a){0===a?c.ymd[0]--:1===a?c.ymd[0]++:2===a?e.YY-=14:e.YY+=14,2>a?(c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2]),c.reshow()):c.viewYears(e.YY)},c.each(b("#laydate_YY .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b),e.tabYear(a)})}),e.tabMonth=function(a){a?(c.ymd[1]++,12===c.ymd[1]&&(c.ymd[0]++,c.ymd[1]=0)):(c.ymd[1]--,-1===c.ymd[1]&&(c.ymd[0]--,c.ymd[1]=11)),c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2])},c.each(b("#laydate_MM .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),e.tabMonth(a)})}),c.each(b("#laydate_ms span"),function(a,b){c.on(b,"click",function(a){c.stopmp(a).reshow(),c.hasClass(this,h[1])||c.viewDate(c.ymd[0],0|this.getAttribute("m"),c.ymd[2])})}),c.each(b("#laydate_table td"),function(a,b){c.on(b,"click",function(a){c.hasClass(this,h[1])||(c.stopmp(a),c.creation([0|this.getAttribute("y"),0|this.getAttribute("m"),0|this.getAttribute("d")]))})}),h.oclear=b("#laydate_clear"),c.on(h.oclear,"click",function(){c.elem[h.elemv]="",c.close()}),h.otoday=b("#laydate_today"),c.on(h.otoday,"click",function(){c.elem[h.elemv]=laydate.now(0,c.options.format),c.close()}),h.ok=b("#laydate_ok"),c.on(h.ok,"click",function(){c.valid&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])}),e.times=b("#laydate_time"),c.hmsin=e.hmsin=b("#laydate_hms input"),e.hmss=["小时","分钟","秒数"],e.hmsarr=[],c.msg=function(a,d){var f='
            '+(d||"提示")+"×
            ";"string"==typeof a?(f+="

            "+a+"

            ",c.shde(b("#"+h[0])),c.removeClass(e.times,"laydate_time1").addClass(e.times,"laydate_msg")):(e.hmsarr[a]?f=e.hmsarr[a]:(f+='
            ',c.each(new Array(0===a?24:60),function(a){f+=""+a+""}),f+="
            ",e.hmsarr[a]=f),c.removeClass(e.times,"laydate_msg"),c[0===a?"removeClass":"addClass"](e.times,"laydate_time1")),c.addClass(e.times,"laydate_show"),e.times.innerHTML=f},e.hmson=function(a,d){var e=b("#laydate_hmsno span"),f=c.valid?null:1;c.each(e,function(b,e){f?c.addClass(e,h[1]):c.timeVoid(b,d)?c.addClass(e,h[1]):c.on(e,"click",function(){c.hasClass(this,h[1])||(a.value=c.digit(0|this.innerHTML))})}),c.addClass(e[0|a.value],"laydate_click")},c.each(e.hmsin,function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.msg(a,e.hmss[a]),e.hmson(this,a)})}),c.on(d,"mouseup",function(){var a=b("#"+h[0]);a&&"none"!==a.style.display&&(c.check()||c.close())}).on(d,"keydown",function(b){b=b||a.event;var d=b.keyCode;13===d&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])})},c.init=function(){c.use("need"),c.use(h[4]+b.defSkin,h[3]),c.skinLink=c.query("#"+h[3])}(),laydate.reset=function(){c.box&&c.elem&&c.follow(c.box)},laydate.now=function(a,b){var d=new Date(0|a?function(a){return 864e5>a?+new Date+864e5*a:a}(parseInt(a)):+new Date);return c.parse([d.getFullYear(),d.getMonth()+1,d.getDate()],[d.getHours(),d.getMinutes(),d.getSeconds()],b)},laydate.skin=function(a){c.skinLink.href=c.getPath+h[4]+a+h[5]}}(window); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/laydate/need/laydate.css ================================================ /** @Name: laydate 核心样式 @Author:贤心 @Site:http://sentsin.com/layui/laydate **/ html{_background-image:url(about:blank); _background-attachment:fixed;} .layer-date{display: inline-block!important;vertical-align:text-top;} .laydate_body .laydate_box, .laydate_body .laydate_box *{margin:0; padding:0;} .laydate-icon, .laydate-icon-default, .laydate-icon-danlan, .laydate-icon-dahong, .laydate-icon-molv{height:34px; padding-right:20px;min-width:34px;vertical-align: text-top;border:1px solid #C6C6C6; background-repeat:no-repeat; background-position:right center; background-color:#fff; outline:0;} .laydate-icon-default{ background-image:url(../skins/default/icon.png)} .laydate-icon-danlan{border:1px solid #B1D2EC; background-image:url(../skins/danlan/icon.png)} .laydate-icon-dahong{background-image:url(../skins/dahong/icon.png)} .laydate-icon-molv{background-image:url(../skins/molv/icon.png)} .laydate_body .laydate_box{width:240px; font:12px '\5B8B\4F53'; z-index:99999999; *margin:-2px 0 0 -2px; *overflow:hidden; _margin:0; _position:absolute!important; background-color:#fff;} .laydate_body .laydate_box li{list-style:none;} .laydate_body .laydate_box .laydate_void{cursor:text!important;} .laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{text-decoration:none; blr:expression(this.onFocus=this.blur()); cursor:pointer;} .laydate_body .laydate_box a:hover{text-decoration:none;} .laydate_body .laydate_box cite, .laydate_body .laydate_box label{position:absolute; width:0; height:0; border-width:5px; border-style:dashed; border-color:transparent; overflow:hidden; cursor:pointer;} .laydate_body .laydate_box .laydate_yms, .laydate_body .laydate_box .laydate_time{display:none;} .laydate_body .laydate_box .laydate_show{display:block;} .laydate_body .laydate_box input{outline:0; font-size:14px; background-color:#fff;} .laydate_body .laydate_top{position:relative; height:26px; padding:5px; *width:100%; z-index:99;} .laydate_body .laydate_ym{position:relative; float:left; height:24px; cursor:pointer;} .laydate_body .laydate_ym input{float:left; height:24px; line-height:24px; text-align:center; border:none; cursor:pointer;} .laydate_body .laydate_ym .laydate_yms{position:absolute; left: -1px; top: 24px; height:181px;} .laydate_body .laydate_y{width:121px;} .laydate_body .laydate_y input{width:64px; margin-right:15px;} .laydate_body .laydate_y .laydate_yms{width:121px; text-align:center;} .laydate_body .laydate_y .laydate_yms a{position:relative; display:block; height:20px;} .laydate_body .laydate_y .laydate_yms ul{height:139px; padding:0; *overflow:hidden;} .laydate_body .laydate_y .laydate_yms ul li{float:left; width:60px; height:20px; line-height: 20px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;} .laydate_box *{box-sizing:content-box!important;} .laydate_body .laydate_m{width:99px;float: right;margin-right:-2px;} .laydate_body .laydate_m .laydate_yms{width:99px; padding:0;} .laydate_body .laydate_m input{width:42px; margin-right:15px;} .laydate_body .laydate_m .laydate_yms span{display:block; float:left; width:42px; margin: 5px 0 0 5px; line-height:24px; text-align:center; _display:inline;} .laydate_body .laydate_choose{display:block; float:left; position:relative; width:20px; height:24px;} .laydate_body .laydate_choose cite, .laydate_body .laydate_tab cite{left:50%; top:50%;} .laydate_body .laydate_chtop cite{margin:-7px 0 0 -5px; border-bottom-style:solid;} .laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{top:50%; margin:-2px 0 0 -5px; border-top-style:solid;} .laydate_body .laydate_chprev cite{margin:-5px 0 0 -7px;} .laydate_body .laydate_chnext cite{margin:-5px 0 0 -2px;} .laydate_body .laydate_ym label{right:28px;} .laydate_body .laydate_table{ width:230px; margin:0 5px; border-collapse:collapse; border-spacing:0px; } .laydate_body .laydate_table td{width:31px; height:19px; line-height:19px; text-align: center; cursor:pointer; font-size: 12px;} .laydate_body .laydate_table thead{height:22px; line-height:22px;} .laydate_body .laydate_table thead th{font-weight:400; font-size:12px; text-align:center;} .laydate_body .laydate_bottom{position:relative; height:22px; line-height:20px; padding:5px; font-size:12px;} .laydate_body .laydate_bottom #laydate_hms{position: relative; z-index: 1; float:left; } .laydate_body .laydate_time{ position:absolute; left:5px; bottom: 26px; width:129px; height:125px; *overflow:hidden;} .laydate_body .laydate_time .laydate_hmsno{ padding:5px 0 0 5px;} .laydate_body .laydate_time .laydate_hmsno span{display:block; float:left; width:24px; height:19px; line-height:19px; text-align:center; cursor:pointer; *margin-bottom:-5px;} .laydate_body .laydate_time1{width:228px; height:154px;} .laydate_body .laydate_time1 .laydate_hmsno{padding: 6px 0 0 8px;} .laydate_body .laydate_time1 .laydate_hmsno span{width:21px; height:20px; line-height:20px;} .laydate_body .laydate_msg{left:49px; bottom:67px; width:141px; height:auto; overflow: hidden;} .laydate_body .laydate_msg p{padding:5px 10px;} .laydate_body .laydate_bottom li{float:left; height:20px; line-height:20px; border-right:none; font-weight:900;} .laydate_body .laydate_bottom .laydate_sj{width:33px; text-align:center; font-weight:400;} .laydate_body .laydate_bottom input{float:left; width:21px; height:20px; line-height:20px; border:none; text-align:center; cursor:pointer; font-size:12px; font-weight:400;} .laydate_body .laydate_bottom .laydte_hsmtex{height:20px; line-height:20px; text-align:center;} .laydate_body .laydate_bottom .laydte_hsmtex span{position:absolute; width:20px; top:0; right:0px; cursor:pointer;} .laydate_body .laydate_bottom .laydte_hsmtex span:hover{font-size:14px;} .laydate_body .laydate_bottom .laydate_btn{position:absolute; right:5px; top:5px;} .laydate_body .laydate_bottom .laydate_btn a{float:left; height:20px; padding:0 6px; _padding:0 5px;} .laydate_body .laydate_bottom .laydate_v{position:absolute; left:10px; top:6px; font-family:Courier; z-index:0;} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/laydate/skins/default/laydate.css ================================================ /** @Name: laydate皮肤:墨绿 @Author:贤心 @Site:http://sentsin.com/layui/laydate **/ .laydate-icon{border:1px solid #ccc; background-image:url(icon.png)} .laydate_body .laydate_bottom #laydate_hms, .laydate_body .laydate_time{border:1px solid #ccc;} .laydate_body .laydate_box, .laydate_body .laydate_ym .laydate_yms, .laydate_body .laydate_time{box-shadow: 2px 2px 5px rgba(0,0,0,.1);} .laydate_body .laydate_box{border-top:none; border-bottom:none; background-color:#fff; color:#00625A;} .laydate_body .laydate_box input{background:none!important; color:#fff;} .laydate_body .laydate_box .laydate_void{color:#00E8D7!important;} .laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{color:#00625A;} .laydate_body .laydate_box a:hover{color:#666;} .laydate_body .laydate_click{background-color:#009F95!important; color:#fff!important;} .laydate_body .laydate_top{border-top:1px solid #009F95; background-color:#009F95} .laydate_body .laydate_ym{border:1px solid #009F95; background-color:#009F95;} .laydate_body .laydate_ym .laydate_yms{border:1px solid #009F95; background-color:#009F95; color:#fff;} .laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #009F95;} .laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #009F95; border-bottom:none;} .laydate_body .laydate_choose{border-left:1px solid #009F95;} .laydate_body .laydate_chprev{border-left:none; border-right:1px solid #009F95;} .laydate_body .laydate_choose:hover, .laydate_body .laydate_y .laydate_yms a:hover{background-color:#00C1B3;} .laydate_body .laydate_chtop cite{border-bottom-color:#fff;} .laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{border-top-color:#fff;} .laydate_body .laydate_chprev cite{border-right-style:solid; border-right-color:#fff;} .laydate_body .laydate_chnext cite{border-left-style:solid; border-left-color:#fff;} .laydate_body .laydate_table{width: 240px!important; margin: 0!important; border:1px solid #ccc; border-top:none; border-bottom:none;} .laydate_body .laydate_table td{border:none; height:21px!important; line-height:21px!important; background-color:#fff; color:#00625A;} .laydate_body .laydate_table .laydate_nothis{color:#999;} .laydate_body .laydate_table thead{border-bottom:1px solid #ccc; height:21px!important; line-height:21px!important;} .laydate_body .laydate_table thead th{} .laydate_body .laydate_bottom{border:1px solid #ccc; border-top:none;} .laydate_body .laydate_bottom #laydate_hms{background-color:#fff;} .laydate_body .laydate_time{background-color:#fff;} .laydate_body .laydate_time1{width: 226px!important; height: 152px!important;} .laydate_body .laydate_bottom .laydate_sj{width:31px!important; border-right:1px solid #ccc; background-color:#fff;} .laydate_body .laydate_bottom input{background-color:#fff; color:#00625A;} .laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #ccc;} .laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #ccc;} .laydate_body .laydate_bottom .laydate_v{color:#999} .laydate_body .laydate_bottom .laydate_btn a{border: 1px solid #ccc; border-right:none; background-color:#fff;} .laydate_body .laydate_bottom .laydate_btn a:hover{background-color:#F6F6F6; color:#00625A;} .laydate_body .laydate_m .laydate_yms span:hover, .laydate_body .laydate_time .laydate_hmsno span:hover, .laydate_body .laydate_y .laydate_yms ul li:hover, .laydate_body .laydate_table td:hover{background-color:#00C1B3; color:#fff;} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/layim/layim.css ================================================ /* @Name: layim WebIM 1.0.0 @Author:贤心(子涵修改) @Date: 2014-04-25 @Blog: http://sentsin.com */ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,input,button,textarea,p,blockquote,th,td,form{margin:0; padding:0;} input,button,textarea,select,optgroup,option{font-family:inherit; font-size:inherit; font-style:inherit; font-weight:inherit; outline: 0;} li{list-style:none;} .xxim_icon, .xxim_main i, .layim_chatbox i{position:absolute;} .loading{background:url(loading.gif) no-repeat center center;} .layim_chatbox a, .layim_chatbox a:hover{color:#343434; text-decoration:none; } .layim_zero{position:absolute; width:0; height:0; border-style:dashed; border-color:transparent; overflow:hidden;} .xxim_main{position:fixed; right:1px; bottom:1px; width:230px; border:1px solid #BEBEBE; background-color:#fff; font-size:12px; box-shadow: 0 0 10px rgba(0,0,0,.2); z-index:99999999} .layim_chatbox textarea{resize:none;} .xxim_main em, .xxim_main i, .layim_chatbox em, .layim_chatbox i{font-style:normal; font-weight:400;} .xxim_main h5{font-size:100%; font-weight:400;} /* 搜索栏 */ .xxim_search{position:relative; padding-left:40px; height:40px; border-bottom:1px solid #DCDCDC; background-color:#fff;} .xxim_search i{left:10px; top:12px; width:16px; height:16px;font-size: 16px;color:#999;} .xxim_search input{border:none; background:none; width: 180px; margin-top:10px; line-height:20px;} .xxim_search span{display:none; position:absolute; right:10px; top:10px; height:18px; line-height:18px;width:18px;text-align: center;background-color:#AFAFAF; color:#fff; cursor:pointer; border-radius:2px; font-size:12px; font-weight:900;} .xxim_search span:hover{background-color:#FCBE00;} /* 主面板tab */ .xxim_tabs{height:45px; border-bottom:1px solid #DBDBDB; background-color:#F4F4F4; font-size:0;} .xxim_tabs span{position:relative; display:inline-block; *display:inline; *zoom:1; vertical-align:top; width:76px; height:45px; border-right:1px solid #DBDBDB; cursor:pointer; font-size:12px;} .xxim_tabs span i{top:12px; left:50%; width:20px; margin-left:-10px; height:20px;font-size:20px;color:#ccc;} .xxim_tabs .xxim_tabnow{height:46px; background-color:#fff;} .xxim_tabs .xxim_tabnow i{color:#1ab394;} .xxim_tabs .xxim_latechat{border-right:none;} .xxim_tabs .xxim_tabfriend i{width:14px; margin-left:-7px;} /* 主面板列表 */ .xxim_list{display:none; height:350px; padding:5px 0; overflow:hidden;} .xxim_list:hover{ overflow-y:auto;} .xxim_list h5{position:relative; padding-left:32px; height:26px; line-height:26px; cursor:pointer; color:#000; font-size:0;} .xxim_list h5 span{display:inline-block; *display:inline; *zoom:1; vertical-align:top; max-width:140px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap; font-size:12px;} .xxim_list h5 i{left:15px; top:8px; width:10px; height:10px;font-size:10px;color:#666;} .xxim_list h5 *{font-size:12px;} .xxim_list .xxim_chatlist{display:none;} .xxim_list .xxim_liston h5 i{width:8px; height:7px;} .xxim_list .xxim_liston .xxim_chatlist{display:block;} .xxim_chatlist {} .xxim_chatlist li{position:relative; height:40px; line-height:30px; padding:5px 10px; font-size:0; cursor:pointer;} .xxim_chatlist li:hover{background-color:#F2F4F8} .xxim_chatlist li *{display:inline-block; *display:inline; *zoom:1; vertical-align:top; font-size:12px;} .xxim_chatlist li span{padding-left:10px; max-width:120px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap;} .xxim_chatlist li img{width:30px; height:30px;} .xxim_chatlist li .xxim_time{position:absolute; right:10px; color:#999;} .xxim_list .xxim_errormsg{text-align:center; margin:50px 0; color:#999;} .xxim_searchmain{position:absolute; width:230px; height:491px; left:0; top:41px; z-index:10; background-color:#fff;} /* 主面板底部 */ .xxim_bottom{height:34px; border-top:1px solid #D0DCF3; background-color:#F2F4F8;} .xxim_expend{border-left:1px solid #D0DCF3; border-bottom:1px solid #D0DCF3;} .xxim_bottom li{position:relative; width:50px; height:32px; line-height:32px; float:left; border-right:1px solid #D0DCF3; cursor:pointer;} .xxim_bottom li i{ top:9px;} .xxim_bottom .xxim_hide{border-right:none;} .xxim_bottom .xxim_online{width:72px; padding-left:35px;} .xxim_online i{left:13px; width:14px; height:14px;font-size:14px;color:#FFA00A;} .xxim_setonline{display:none; position:absolute; left:-79px; bottom:-1px; border:1px solid #DCDCDC; background-color:#fff;} .xxim_setonline span{position:relative; display:block; width:32px;width: 77px; padding:0 10px 0 35px;} .xxim_setonline span:hover{background-color:#F2F4F8;} .xxim_offline .xxim_nowstate, .xxim_setoffline i{color:#999;} .xxim_mymsg i{left:18px; width:14px; height:14px;font-size: 14px;} .xxim_mymsg a{position:absolute; left:0; top:0; width:50px; height:32px;} .xxim_seter i{left:18px; width:14px; height:14px;font-size: 14px;} .xxim_hide i{left:18px; width:14px; height:14px;font-size: 14px;} .xxim_show i{} .xxim_bottom .xxim_on{position:absolute; left:-17px; top:50%; width:16px;text-align: center;color:#999;line-height: 97px; height:97px; margin-top:-49px;border:solid 1px #BEBEBE;border-right: none; background:#F2F4F8;} .xxim_bottom .xxim_off{} /* 聊天窗口 */ .layim_chatbox{width:620px; border:1px solid #BEBEBE; background-color:#fff; font-size:12px; box-shadow: 0 0 10px rgba(0,0,0,.2);} .layim_chatbox h6{position:relative; height:40px; border-bottom:1px solid #D9D9D9; background-color:#FCFDFA} .layim_move{position:absolute; height:40px; width: 620px; z-index:0;} .layim_face{position:absolute; bottom:-1px; left:10px; width:64px; height:64px;padding:1px;background: #fff; border:1px solid #ccc;} .layim_face img{width:60px; height:60px;} .layim_names{position:absolute; left:90px; max-width:300px; line-height:40px; color:#000; overflow:hidden; text-overflow: ellipsis; white-space:nowrap; font-size:14px;} .layim_rightbtn{position:absolute; right:15px; top:12px; font-size:20px;} .layim_rightbtn i{position:relative; width:16px; height:16px; display:inline-block; *display:inline; *zoom:1; vertical-align:top; cursor:pointer; transition: all .3s;text-align: center;line-height: 16px;} .layim_rightbtn .layim_close{background: #FFA00A;color:#fff;} .layim_rightbtn .layim_close:hover{-webkit-transform: rotate(180deg); -moz-transform: rotate(180deg);} .layim_rightbtn .layer_setmin{margin-right:5px;color:#999;font-size:14px;font-weight: 700;} .layim_chat, .layim_chatmore,.layim_groups{height:450px; overflow:hidden;} .layim_chatmore{display:none; float:left; width:135px; border-right:1px solid #BEBEBE; background-color:#F2F2F2} .layim_chatlist li, .layim_groups li{position:relative; height:30px; line-height:30px; padding:0 10px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap; cursor:pointer;} .layim_chatlist li{padding:0 20px 0 10px;} .layim_chatlist li:hover{background-color:#E3E3E3;} .layim_chatlist li span{display:inline-block; *display:inline; *zoom:1; vertical-align:top; width:90px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap;} .layim_chatlist li em{display:none; position:absolute; top:6px; right:10px; height:18px; line-height:18px;width:18px;text-align: center;font-size:14px;font-weight:900; border-radius:3px;} .layim_chatlist li em:hover{background-color: #FCBE00; color:#fff;} .layim_chatlist .layim_chatnow,.layim_chatlist .layim_chatnow:hover{/*border-top:1px solid #D9D9D9; border-bottom:1px solid #D9D9D9;*/ background-color:#fff;} .layim_chat{} .layim_chatarea{height:280px;} .layim_chatview{display:none; height:280px; overflow:hidden;} .layim_chatmore:hover, .layim_groups:hover, .layim_chatview:hover{overflow-y:auto;} .layim_chatview li{margin-bottom:10px; clear:both; *zoom:1;} .layim_chatview li:after{content:'\20'; clear:both; *zoom:1; display:block; height:0;} .layim_chatthis{display:block;} .layim_chatuser{float:left; padding:15px; font-size:0;} .layim_chatuser *{display:inline-block; *display:inline; *zoom:1; vertical-align:top; line-height:30px; font-size:12px; padding-right:10px;} .layim_chatuser img{width:30px; height:30px;padding-right: 0;margin-right: 15px;} .layim_chatuser .layim_chatname{max-width:230px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap;} .layim_chatuser .layim_chattime{color:#999; padding-left:10px;} .layim_chatsay{position:relative; float:left; margin:0 15px; padding:10px; line-height:20px; background-color:#F3F3F3; border-radius:3px; clear:both;} .layim_chatsay .layim_zero{left:5px; top:-8px; border-width:8px; border-right-style:solid; border-right-color:#F3F3F3;} .layim_chateme .layim_chatuser{float:right;} .layim_chateme .layim_chatuser *{padding-right:0; padding-left:10px;} .layim_chateme .layim_chatuser img{margin-left:15px;padding-left: 0;} .layim_chateme .layim_chatsay .layim_zero{left:auto; right:10px;} .layim_chateme .layim_chatuser .layim_chattime{padding-left:0; padding-right:10px;} .layim_chateme .layim_chatsay{float:right; background-color:#EBFBE3} .layim_chateme .layim_zero{border-right-color:#EBFBE3;} .layim_groups{display:none; float:right; width:130px; border-left:1px solid #D9D9D9; background-color:#fff;} .layim_groups ul{display:none;} .layim_groups ul.layim_groupthis{display:block;} .layim_groups li *{display:inline-block; *display:inline; *zoom:1; vertical-align:top; margin-right:10px;} .layim_groups li img{width:20px; height:20px; margin-top:5px;} .layim_groups li span{max-width:80px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap;} .layim_groups li:hover{background-color:#F3F3F3;} .layim_groups .layim_errors{text-align:center; color:#999;} .layim_tool{position:relative; height:35px; line-height:35px; padding-left:10px; background-color:#F3F3F3;} .layim_tool i{position:relative; top:10px; display:inline-block; *display:inline; *zoom:1; vertical-align:top; width:16px; height:16px; margin-right:10px; cursor:pointer;font-size:16px;color:#999;font-weight: 700;} .layim_tool i:hover{color:#FFA00A;} .layim_tool .layim_seechatlog{position:absolute; right:15px;} .layim_tool .layim_seechatlog i{} .layim_write{display:block; border:none; width:98%; height:90px; line-height:20px; margin:5px auto 0;} .layim_send{position:relative; height:40px; background-color:#F3F3F3;} .layim_sendbtn{position:absolute; height:26px; line-height:26px; right:10px; top:8px; padding:0 40px 0 20px; background-color:#FFA00A; color:#fff; border-radius:3px; cursor:pointer;} .layim_enter{position:absolute; right:0; border-left:1px solid #FFB94F; width:24px; height:26px;} .layim_enter:hover{background-color:#E68A00; border-radius:0 3px 3px 0;} .layim_enter .layim_zero{left:7px; top:11px; border-width:5px; border-top-style:solid; border-top-color:#FFE0B3;} .layim_sendtype{display:none; position:absolute; right:10px; bottom:37px; border:1px solid #D9D9D9; background-color:#fff; text-align:left;} .layim_sendtype span{display:block; line-height:24px; padding:0 10px 0 25px; cursor:pointer;} .layim_sendtype span:hover{background-color:#F3F3F3;} .layim_sendtype span i{left:5px;} .layim_min{display:none; position:absolute; left:-190px; bottom:-1px; width:160px; height:32px; line-height:32px; padding:0 10px; overflow:hidden; text-overflow: ellipsis; white-space:nowrap; border:1px solid #ccc; box-shadow: 0 0 5px rgba(0,0,75,.2); background-color:#FCFDFA; cursor:pointer;} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/layim/layim.js ================================================ /* @Name: layui WebIM 1.0.0 @Author:贤心 @Date: 2014-04-25 @Blog: http://sentsin.com */ ;!function(win, undefined){ var config = { msgurl: 'mailbox.html?msg=', chatlogurl: 'mailbox.html?user=', aniTime: 200, right: -232, api: { friend: 'js/plugins/layer/layim/data/friend.json', //好友列表接口 group: 'js/plugins/layer/layim/data/group.json', //群组列表接口 chatlog: 'js/plugins/layer/layim/data/chatlog.json', //聊天记录接口 groups: 'js/plugins/layer/layim/data/groups.json', //群组成员接口 sendurl: '' //发送消息接口 }, user: { //当前用户信息 name: '游客', face: 'img/a1.jpg' }, //自动回复内置文案,也可动态读取数据库配置 autoReplay: [ '您好,我现在有事不在,一会再和您联系。', '你没发错吧?', '洗澡中,请勿打扰,偷窥请购票,个体四十,团体八折,订票电话:一般人我不告诉他!', '你好,我是主人的美女秘书,有什么事就跟我说吧,等他回来我会转告他的。', '我正在拉磨,没法招呼您,因为我们家毛驴去动物保护协会把我告了,说我剥夺它休产假的权利。', '<(@ ̄︶ ̄@)>', '你要和我说话?你真的要和我说话?你确定自己想说吗?你一定非说不可吗?那你说吧,这是自动回复。', '主人正在开机自检,键盘鼠标看好机会出去凉快去了,我是他的电冰箱,我打字比较慢,你慢慢说,别急……', '(*^__^*) 嘻嘻,是贤心吗?' ], chating: {}, hosts: (function(){ var dk = location.href.match(/\:\d+/); dk = dk ? dk[0] : ''; return 'http://' + document.domain + dk + '/'; })(), json: function(url, data, callback, error){ return $.ajax({ type: 'POST', url: url, data: data, dataType: 'json', success: callback, error: error }); }, stopMP: function(e){ e ? e.stopPropagation() : e.cancelBubble = true; } }, dom = [$(window), $(document), $('html'), $('body')], xxim = {}; //主界面tab xxim.tabs = function(index){ var node = xxim.node; node.tabs.eq(index).addClass('xxim_tabnow').siblings().removeClass('xxim_tabnow'); node.list.eq(index).show().siblings('.xxim_list').hide(); if(node.list.eq(index).find('li').length === 0){ xxim.getDates(index); } }; //节点 xxim.renode = function(){ var node = xxim.node = { tabs: $('#xxim_tabs>span'), list: $('.xxim_list'), online: $('.xxim_online'), setonline: $('.xxim_setonline'), onlinetex: $('#xxim_onlinetex'), xximon: $('#xxim_on'), layimFooter: $('#xxim_bottom'), xximHide: $('#xxim_hide'), xximSearch: $('#xxim_searchkey'), searchMian: $('#xxim_searchmain'), closeSearch: $('#xxim_closesearch'), layimMin: $('#layim_min') }; }; //主界面缩放 xxim.expend = function(){ var node = xxim.node; if(xxim.layimNode.attr('state') !== '1'){ xxim.layimNode.stop().animate({right: config.right}, config.aniTime, function(){ node.xximon.addClass('xxim_off'); try{ localStorage.layimState = 1; }catch(e){} xxim.layimNode.attr({state: 1}); node.layimFooter.addClass('xxim_expend').stop().animate({marginLeft: config.right}, config.aniTime/2); node.xximHide.addClass('xxim_show'); }); } else { xxim.layimNode.stop().animate({right: 1}, config.aniTime, function(){ node.xximon.removeClass('xxim_off'); try{ localStorage.layimState = 2; }catch(e){} xxim.layimNode.removeAttr('state'); node.layimFooter.removeClass('xxim_expend'); node.xximHide.removeClass('xxim_show'); }); node.layimFooter.stop().animate({marginLeft: 0}, config.aniTime); } }; //初始化窗口格局 xxim.layinit = function(){ var node = xxim.node; //主界面 try{ /* if(!localStorage.layimState){ config.aniTime = 0; localStorage.layimState = 1; } */ if(localStorage.layimState === '1'){ xxim.layimNode.attr({state: 1}).css({right: config.right}); node.xximon.addClass('xxim_off'); node.layimFooter.addClass('xxim_expend').css({marginLeft: config.right}); node.xximHide.addClass('xxim_show'); } }catch(e){ //layer.msg(e.message, 5, -1); } }; //聊天窗口 xxim.popchat = function(param){ var node = xxim.node, log = {}; log.success = function(layero){ layer.setMove(); xxim.chatbox = layero.find('#layim_chatbox'); log.chatlist = xxim.chatbox.find('.layim_chatmore>ul'); log.chatlist.html('
          • '+ param.name +'×
          • ') xxim.tabchat(param, xxim.chatbox); //最小化聊天窗 xxim.chatbox.find('.layer_setmin').on('click', function(){ var indexs = layero.attr('times'); layero.hide(); node.layimMin.text(xxim.nowchat.name).show(); }); //关闭窗口 xxim.chatbox.find('.layim_close').on('click', function(){ var indexs = layero.attr('times'); layer.close(indexs); xxim.chatbox = null; config.chating = {}; config.chatings = 0; }); //关闭某个聊天 log.chatlist.on('mouseenter', 'li', function(){ $(this).find('em').show(); }).on('mouseleave', 'li', function(){ $(this).find('em').hide(); }); log.chatlist.on('click', 'li em', function(e){ var parents = $(this).parent(), dataType = parents.attr('type'); var dataId = parents.attr('data-id'), index = parents.index(); var chatlist = log.chatlist.find('li'), indexs; config.stopMP(e); delete config.chating[dataType + dataId]; config.chatings--; parents.remove(); $('#layim_area'+ dataType + dataId).remove(); if(dataType === 'group'){ $('#layim_group'+ dataType + dataId).remove(); } if(parents.hasClass('layim_chatnow')){ if(index === config.chatings){ indexs = index - 1; } else { indexs = index + 1; } xxim.tabchat(config.chating[chatlist.eq(indexs).attr('type') + chatlist.eq(indexs).attr('data-id')]); } if(log.chatlist.find('li').length === 1){ log.chatlist.parent().hide(); } }); //聊天选项卡 log.chatlist.on('click', 'li', function(){ var othis = $(this), dataType = othis.attr('type'), dataId = othis.attr('data-id'); xxim.tabchat(config.chating[dataType + dataId]); }); //发送热键切换 log.sendType = $('#layim_sendtype'), log.sendTypes = log.sendType.find('span'); $('#layim_enter').on('click', function(e){ config.stopMP(e); log.sendType.show(); }); log.sendTypes.on('click', function(){ log.sendTypes.find('i').text('') $(this).find('i').text('√'); }); xxim.transmit(); }; log.html = '
            ' +'
            ' +'' +' ' +' '+ param.name +'' +' ' +' ' +' ×' +' ' +'
            ' +'
            ' +'
              ' +'
              ' +'
              ' +'
              ' +'
              ' +'
                ' +'
                ' +'
                ' +' ' +' ' +' ' +' 聊天记录' +'
                ' +' ' +'
                ' +'
                发送
                ' +'
                ' +' 按Enter键发送' +' 按Ctrl+Enter键发送' +'
                ' +'
                ' +'
                ' +'
                '; if(config.chatings < 1){ $.layer({ type: 1, border: [0], title: false, shade: [0], area: ['620px', '493px'], move: '.layim_chatbox .layim_move', moveType: 1, closeBtn: false, offset: [(($(window).height() - 493)/2)+'px', ''], page: { html: log.html }, success: function(layero){ log.success(layero); } }) } else { log.chatmore = xxim.chatbox.find('#layim_chatmore'); log.chatarea = xxim.chatbox.find('#layim_chatarea'); log.chatmore.show(); log.chatmore.find('ul>li').removeClass('layim_chatnow'); log.chatmore.find('ul').append('
              • '+ param.name +'×
              • '); log.chatarea.find('.layim_chatview').removeClass('layim_chatthis'); log.chatarea.append('
                  '); xxim.tabchat(param); } //群组 log.chatgroup = xxim.chatbox.find('#layim_groups'); if(param.type === 'group'){ log.chatgroup.find('ul').removeClass('layim_groupthis'); log.chatgroup.append('
                    '); xxim.getGroups(param); } //点击群员切换聊天窗 log.chatgroup.on('click', 'ul>li', function(){ xxim.popchatbox($(this)); }); }; //定位到某个聊天队列 xxim.tabchat = function(param){ var node = xxim.node, log = {}, keys = param.type + param.id; xxim.nowchat = param; xxim.chatbox.find('#layim_user'+ keys).addClass('layim_chatnow').siblings().removeClass('layim_chatnow'); xxim.chatbox.find('#layim_area'+ keys).addClass('layim_chatthis').siblings().removeClass('layim_chatthis'); xxim.chatbox.find('#layim_group'+ keys).addClass('layim_groupthis').siblings().removeClass('layim_groupthis'); xxim.chatbox.find('.layim_face>img').attr('src', param.face); xxim.chatbox.find('.layim_face, .layim_names').attr('href', param.href); xxim.chatbox.find('.layim_names').text(param.name); xxim.chatbox.find('.layim_seechatlog').attr('href', config.chatlogurl + param.id); log.groups = xxim.chatbox.find('.layim_groups'); if(param.type === 'group'){ log.groups.show(); } else { log.groups.hide(); } $('#layim_write').focus(); }; //弹出聊天窗 xxim.popchatbox = function(othis){ var node = xxim.node, dataId = othis.attr('data-id'), param = { id: dataId, //用户ID type: othis.attr('type'), name: othis.find('.xxim_onename').text(), //用户名 face: othis.find('.xxim_oneface').attr('src'), //用户头像 href: 'profile.html?user=' + dataId //用户主页 }, key = param.type + dataId; if(!config.chating[key]){ xxim.popchat(param); config.chatings++; } else { xxim.tabchat(param); } config.chating[key] = param; var chatbox = $('#layim_chatbox'); if(chatbox[0]){ node.layimMin.hide(); chatbox.parents('.xubox_layer').show(); } }; //请求群员 xxim.getGroups = function(param){ var keys = param.type + param.id, str = '', groupss = xxim.chatbox.find('#layim_group'+ keys); groupss.addClass('loading'); config.json(config.api.groups, {}, function(datas){ if(datas.status === 1){ var ii = 0, lens = datas.data.length; if(lens > 0){ for(; ii < lens; ii++){ str += '
                  • '+ datas.data[ii].name +'
                  • '; } } else { str = '
                  • 没有群员
                  • '; } } else { str = '
                  • '+ datas.msg +'
                  • '; } groupss.removeClass('loading'); groupss.html(str); }, function(){ groupss.removeClass('loading'); groupss.html('
                  • 请求异常
                  • '); }); }; //消息传输 xxim.transmit = function(){ var node = xxim.node, log = {}; node.sendbtn = $('#layim_sendbtn'); node.imwrite = $('#layim_write'); //发送 log.send = function(){ var data = { content: node.imwrite.val(), id: xxim.nowchat.id, sign_key: '', //密匙 _: +new Date }; if(data.content.replace(/\s/g, '') === ''){ layer.tips('说点啥呗!', '#layim_write', 2); node.imwrite.focus(); } else { //此处皆为模拟 var keys = xxim.nowchat.type + xxim.nowchat.id; //聊天模版 log.html = function(param, type){ return '
                  • ' +'
                    ' + function(){ if(type === 'me'){ return ''+ param.time +'' +''+ param.name +'' +''; } else { return '' +''+ param.name +'' +''+ param.time +''; } }() +'
                    ' +'
                    '+ param.content +'
                    ' +'
                  • '; }; log.imarea = xxim.chatbox.find('#layim_area'+ keys); log.imarea.append(log.html({ time: '2014-04-26 0:37', name: config.user.name, face: config.user.face, content: data.content }, 'me')); node.imwrite.val('').focus(); log.imarea.scrollTop(log.imarea[0].scrollHeight); setTimeout(function(){ log.imarea.append(log.html({ time: '2014-04-26 0:38', name: xxim.nowchat.name, face: xxim.nowchat.face, content: config.autoReplay[(Math.random()*config.autoReplay.length) | 0] })); log.imarea.scrollTop(log.imarea[0].scrollHeight); }, 500); /* that.json(config.api.sendurl, data, function(datas){ }); */ } }; node.sendbtn.on('click', log.send); node.imwrite.keyup(function(e){ if(e.keyCode === 13){ log.send(); } }); }; //事件 xxim.event = function(){ var node = xxim.node; //主界面tab node.tabs.eq(0).addClass('xxim_tabnow'); node.tabs.on('click', function(){ var othis = $(this), index = othis.index(); xxim.tabs(index); }); //列表展收 node.list.on('click', 'h5', function(){ var othis = $(this), chat = othis.siblings('.xxim_chatlist'), parentss = othis.find("i"); if(parentss.hasClass('fa-caret-down')){ chat.hide(); parentss.attr('class','fa fa-caret-right'); } else { chat.show(); parentss.attr('class','fa fa-caret-down'); } }); //设置在线隐身 node.online.on('click', function(e){ config.stopMP(e); node.setonline.show(); }); node.setonline.find('span').on('click', function(e){ var index = $(this).index(); config.stopMP(e); if(index === 0){ node.onlinetex.html('在线'); node.online.removeClass('xxim_offline'); } else if(index === 1) { node.onlinetex.html('隐身'); node.online.addClass('xxim_offline'); } node.setonline.hide(); }); node.xximon.on('click', xxim.expend); node.xximHide.on('click', xxim.expend); //搜索 node.xximSearch.keyup(function(){ var val = $(this).val().replace(/\s/g, ''); if(val !== ''){ node.searchMian.show(); node.closeSearch.show(); //此处的搜索ajax参考xxim.getDates node.list.eq(3).html('
                  • 没有符合条件的结果
                  • '); } else { node.searchMian.hide(); node.closeSearch.hide(); } }); node.closeSearch.on('click', function(){ $(this).hide(); node.searchMian.hide(); node.xximSearch.val('').focus(); }); //弹出聊天窗 config.chatings = 0; node.list.on('click', '.xxim_childnode', function(){ var othis = $(this); xxim.popchatbox(othis); }); //点击最小化栏 node.layimMin.on('click', function(){ $(this).hide(); $('#layim_chatbox').parents('.xubox_layer').show(); }); //document事件 dom[1].on('click', function(){ node.setonline.hide(); $('#layim_sendtype').hide(); }); }; //请求列表数据 xxim.getDates = function(index){ var api = [config.api.friend, config.api.group, config.api.chatlog], node = xxim.node, myf = node.list.eq(index); myf.addClass('loading'); config.json(api[index], {}, function(datas){ if(datas.status === 1){ var i = 0, myflen = datas.data.length, str = '', item; if(myflen > 1){ if(index !== 2){ for(; i < myflen; i++){ str += '
                  • ' +'
                    '+ datas.data[i].name +'('+ datas.data[i].nums +')
                    ' +'
                      '; item = datas.data[i].item; for(var j = 0; j < item.length; j++){ str += '
                    • '+ item[j].name +'
                    • '; } str += '
                  • '; } } else { str += '
                  • ' +'
                      '; for(; i < myflen; i++){ str += '
                    • '+ datas.data[i].name +''+ datas.data[i].time +'
                    • '; } str += '
                  • '; } myf.html(str); } else { myf.html('
                  • 没有任何数据
                  • '); } myf.removeClass('loading'); } else { myf.html('
                  • '+ datas.msg +'
                  • '); } }, function(){ myf.html('
                  • 请求失败
                  • '); myf.removeClass('loading'); }); }; //渲染骨架 xxim.view = (function(){ var xximNode = xxim.layimNode = $('
                    ' +'
                    ' +' ' +'
                    ' +'
                      ' +'
                        ' +'
                          ' +'
                            ' +'
                            ' +'
                              ' +'
                            • ' +'在线' +'
                              ' +'在线' +'隐身' +'
                              ' +'
                            • ' +'
                            • ' +'
                            • ' +'' +'
                              ' +'
                              ' +'
                            • ' +'
                            • ' +'
                            • ' +'
                              ' +'
                            ' +'
                            '); dom[3].append(xximNode); xxim.renode(); xxim.getDates(0); xxim.event(); xxim.layinit(); }()); }(window); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/mobile/layer.js ================================================ /*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */ ;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'

                            '+(e?n.title[0]:n.title)+"

                            ":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e=''+n.btn[0]+"",2===t&&(e=''+n.btn[1]+""+e),'
                            '+e+"
                            "):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='

                            '+(n.content||"")+"

                            "),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"
                            ':"")+'
                            "+l+'
                            '+n.content+"
                            "+c+"
                            ",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;odiv{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/skin/layer.css ================================================ /*! @Name: layer's style @Author: 贤心 @Blog: sentsin.com */*html{background-image:url(about:blank);background-attachment:fixed}html #layui_layer_skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{top:150px;left:50%;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3);border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.3);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-moves{position:absolute;border:3px solid #666;border:3px solid rgba(0,0,0,.5);cursor:move;background-color:#fff;background-color:rgba(255,255,255,.3);filter:alpha(opacity=50)}.layui-layer-load{background:url(default/loading-0.gif) center center no-repeat #fff}.layui-layer-ico{background:url(default/icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);-ms-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layui-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layui-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layui-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#F8F8F8}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-150px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-181px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px solid #dedede;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.7}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;font-size:14px;overflow:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe .layui-layer-content{overflow:hidden}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(default/loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(default/loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(default/loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.3);background-color:#F90;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#F90}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#F90}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#BBB5B5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/skin/layer.ext.css ================================================ /*! @Name: layer拓展样式 @Date: 2012.12.13 @Author: 贤心 @blog: sentsin.com */.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span{text-overflow:ellipsis;white-space:nowrap}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}html #layui_layer_skinlayerextcss{display:none;position:absolute;width:1989px}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s;background:url(default/xubox_loading1.gif) center center no-repeat #000}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/layer/theme/default/layer.css ================================================ .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/validate/zh_CN.js ================================================ (function ($) { /** * Simplified Chinese language package * Translated by @shamiao */ $.fn.bootstrapValidator.i18n = $.extend(true, $.fn.bootstrapValidator.i18n, { base64: { 'default': '请输入有效的Base64编码' }, between: { 'default': '请输入在 %s 和 %s 之间的数值', notInclusive: '请输入在 %s 和 %s 之间(不含两端)的数值' }, callback: { 'default': '请输入有效的值' }, choice: { 'default': '请输入有效的值', less: '请至少选中 %s 个选项', more: '最多只能选中 %s 个选项', between: '请选择 %s 至 %s 个选项' }, color: { 'default': '请输入有效的颜色值' }, creditCard: { 'default': '请输入有效的信用卡号码' }, cusip: { 'default': '请输入有效的美国CUSIP代码' }, cvv: { 'default': '请输入有效的CVV代码' }, date: { 'default': '请输入有效的日期', min: '请输入 %s 或之后的日期', max: '请输入 %s 或以前的日期', range: '请输入 %s 和 %s 之间的日期' }, different: { 'default': '请输入不同的值' }, digits: { 'default': '请输入有效的数字' }, ean: { 'default': '请输入有效的EAN商品编码' }, emailAddress: { 'default': '请输入有效的邮件地址' }, file: { 'default': '请选择有效的文件' }, greaterThan: { 'default': '请输入大于等于 %s 的数值', notInclusive: '请输入大于 %s 的数值' }, grid: { 'default': '请输入有效的GRId编码' }, hex: { 'default': '请输入有效的16进制数' }, hexColor: { 'default': '请输入有效的16进制颜色值' }, iban: { 'default': '请输入有效的IBAN(国际银行账户)号码', countryNotSupported: '不支持 %s 国家或地区', country: '请输入有效的 %s 国家或地区的IBAN(国际银行账户)号码', countries: { AD: '安道​​尔', AE: '阿联酋', AL: '阿尔巴尼亚', AO: '安哥拉', AT: '奥地利', AZ: '阿塞拜疆', BA: '波斯尼亚和黑塞哥维那', BE: '比利时', BF: '布基纳法索', BG: '保加利亚', BH: '巴林', BI: '布隆迪', BJ: '贝宁', BR: '巴西', CH: '瑞士', CI: '科特迪瓦', CM: '喀麦隆', CR: '哥斯达黎加', CV: '佛得角', CY: '塞浦路斯', CZ: '捷克共和国', DE: '德国', DK: '丹麦', DO: '多米尼加共和国', DZ: '阿尔及利亚', EE: '爱沙尼亚', ES: '西班牙', FI: '芬兰', FO: '法罗群岛', FR: '法国', GB: '英国', GE: '格鲁吉亚', GI: '直布罗陀', GL: '格陵兰岛', GR: '希腊', GT: '危地马拉', HR: '克罗地亚', HU: '匈牙利', IE: '爱尔兰', IL: '以色列', IR: '伊朗', IS: '冰岛', IT: '意大利', JO: '约旦', KW: '科威特', KZ: '哈萨克斯坦', LB: '黎巴嫩', LI: '列支敦士登', LT: '立陶宛', LU: '卢森堡', LV: '拉脱维亚', MC: '摩纳哥', MD: '摩尔多瓦', ME: '黑山', MG: '马达加斯加', MK: '马其顿', ML: '马里', MR: '毛里塔尼亚', MT: '马耳他', MU: '毛里求斯', MZ: '莫桑比克', NL: '荷兰', NO: '挪威', PK: '巴基斯坦', PL: '波兰', PS: '巴勒斯坦', PT: '葡萄牙', QA: '卡塔尔', RO: '罗马尼亚', RS: '塞尔维亚', SA: '沙特阿拉伯', SE: '瑞典', SI: '斯洛文尼亚', SK: '斯洛伐克', SM: '圣马力诺', SN: '塞内加尔', TN: '突尼斯', TR: '土耳其', VG: '英属维尔京群岛' } }, id: { 'default': '请输入有效的身份证件号码', countryNotSupported: '不支持 %s 国家或地区', country: '请输入有效的 %s 国家或地区的身份证件号码', countries: { BA: '波黑', BG: '保加利亚', BR: '巴西', CH: '瑞士', CL: '智利', CN: '中国', CZ: '捷克共和国', DK: '丹麦', EE: '爱沙尼亚', ES: '西班牙', FI: '芬兰', HR: '克罗地亚', IE: '爱尔兰', IS: '冰岛', LT: '立陶宛', LV: '拉脱维亚', ME: '黑山', MK: '马其顿', NL: '荷兰', RO: '罗马尼亚', RS: '塞尔维亚', SE: '瑞典', SI: '斯洛文尼亚', SK: '斯洛伐克', SM: '圣马力诺', TH: '泰国', ZA: '南非' } }, identical: { 'default': '请输入相同的值' }, imei: { 'default': '请输入有效的IMEI(手机串号)' }, imo: { 'default': '请输入有效的国际海事组织(IMO)号码' }, integer: { 'default': '请输入有效的整数值' }, ip: { 'default': '请输入有效的IP地址', ipv4: '请输入有效的IPv4地址', ipv6: '请输入有效的IPv6地址' }, isbn: { 'default': '请输入有效的ISBN(国际标准书号)' }, isin: { 'default': '请输入有效的ISIN(国际证券编码)' }, ismn: { 'default': '请输入有效的ISMN(印刷音乐作品编码)' }, issn: { 'default': '请输入有效的ISSN(国际标准杂志书号)' }, lessThan: { 'default': '请输入小于等于 %s 的数值', notInclusive: '请输入小于 %s 的数值' }, mac: { 'default': '请输入有效的MAC物理地址' }, meid: { 'default': '请输入有效的MEID(移动设备识别码)' }, notEmpty: { 'default': '请填写必填项目' }, numeric: { 'default': '请输入有效的数值,允许小数' }, phone: { 'default': '请输入有效的电话号码', countryNotSupported: '不支持 %s 国家或地区', country: '请输入有效的 %s 国家或地区的电话号码', countries: { BR: '巴西', CN: '中国', CZ: '捷克共和国', DE: '德国', DK: '丹麦', ES: '西班牙', FR: '法国', GB: '英国', MA: '摩洛哥', PK: '巴基斯坦', RO: '罗马尼亚', RU: '俄罗斯', SK: '斯洛伐克', TH: '泰国', US: '美国', VE: '委内瑞拉' } }, regexp: { 'default': '请输入符合正则表达式限制的值' }, remote: { 'default': '请输入有效的值' }, rtn: { 'default': '请输入有效的RTN号码' }, sedol: { 'default': '请输入有效的SEDOL代码' }, siren: { 'default': '请输入有效的SIREN号码' }, siret: { 'default': '请输入有效的SIRET号码' }, step: { 'default': '请输入在基础值上,增加 %s 的整数倍的数值' }, stringCase: { 'default': '只能输入小写字母', upper: '只能输入大写字母' }, stringLength: { 'default': '请输入符合长度限制的值', less: '最多只能输入 %s 个字符', more: '需要输入至少 %s 个字符', between: '请输入 %s 至 %s 个字符' }, uri: { 'default': '请输入一个有效的URL地址' }, uuid: { 'default': '请输入有效的UUID', version: '请输入版本 %s 的UUID' }, vat: { 'default': '请输入有效的VAT(税号)', countryNotSupported: '不支持 %s 国家或地区', country: '请输入有效的 %s 国家或地区的VAT(税号)', countries: { AT: '奥地利', BE: '比利时', BG: '保加利亚', BR: '巴西', CH: '瑞士', CY: '塞浦路斯', CZ: '捷克共和国', DE: '德国', DK: '丹麦', EE: '爱沙尼亚', ES: '西班牙', FI: '芬兰', FR: '法语', GB: '英国', GR: '希腊', EL: '希腊', HU: '匈牙利', HR: '克罗地亚', IE: '爱尔兰', IS: '冰岛', IT: '意大利', LT: '立陶宛', LU: '卢森堡', LV: '拉脱维亚', MT: '马耳他', NL: '荷兰', NO: '挪威', PL: '波兰', PT: '葡萄牙', RO: '罗马尼亚', RU: '俄罗斯', RS: '塞尔维亚', SE: '瑞典', SI: '斯洛文尼亚', SK: '斯洛伐克', VE: '委内瑞拉', ZA: '南非' } }, vin: { 'default': '请输入有效的VIN(美国车辆识别号码)' }, zipCode: { 'default': '请输入有效的邮政编码', countryNotSupported: '不支持 %s 国家或地区', country: '请输入有效的 %s 国家或地区的邮政编码', countries: { AT: '奥地利', BR: '巴西', CA: '加拿大', CH: '瑞士', CZ: '捷克共和国', DE: '德国', DK: '丹麦', FR: '法国', GB: '英国', IE: '爱尔兰', IT: '意大利', MA: '摩洛哥', NL: '荷兰', PT: '葡萄牙', RO: '罗马尼亚', RU: '俄罗斯', SE: '瑞典', SG: '新加坡', SK: '斯洛伐克', US: '美国' } } }); }(window.jQuery)); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/README.md ================================================ 目录说明 ======================== ```bash ├── Uploader.swf # SWF文件,当使用Flash运行时需要引入。 ├ ├── webuploader.js # 完全版本。 ├── webuploader.min.js # min版本 ├ ├── webuploader.flashonly.js # 只有Flash实现的版本。 ├── webuploader.flashonly.min.js # min版本 ├ ├── webuploader.html5only.js # 只有Html5实现的版本。 ├── webuploader.html5only.min.js # min版本 ├ ├── webuploader.noimage.js # 去除图片处理的版本,包括HTML5和FLASH. ├── webuploader.noimage.min.js # min版本 ├ ├── webuploader.custom.js # 自定义打包方案,请查看 Gruntfile.js,满足移动端使用。 └── webuploader.custom.min.js # min版本 ``` ## 示例 请把整个 Git 包下载下来放在 php 服务器下,因为默认提供的文件接受是用 php 编写的,打开 examples 页面便能查看示例效果。 ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.css ================================================ .webuploader-container { position: relative; } .webuploader-element-invisible { position: absolute !important; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px,1px,1px,1px); } .webuploader-pick { position: relative; display: inline-block; cursor: pointer; background: #00b7ee; padding: 10px 15px; color: #fff; text-align: center; border-radius: 3px; overflow: hidden; } .webuploader-pick-hover { background: #00a2d4; } .webuploader-pick-disable { opacity: 0.6; pointer-events:none; } ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.custom.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * 直接来源于jquery的代码。 * @fileOverview Promise/A+ * @beta */ define('promise-builtin',[ 'dollar' ], function( $ ) { var api; // 简单版Callbacks, 默认memory,可选once. function Callbacks( once ) { var list = [], stack = !once && [], fire = function( data ) { memory = data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ); } firing = false; if ( list ) { if ( stack ) { stack.length && fire( stack.shift() ); } else { list = []; } } }, self = { add: function() { if ( list ) { var start = list.length; (function add ( args ) { $.each( args, function( _, arg ) { var type = $.type( arg ); if ( type === 'function' ) { list.push( arg ); } else if ( arg && arg.length && type !== 'string' ) { add( arg ); } }); })( arguments ); if ( firing ) { firingLength = list.length; } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, disable: function() { list = stack = memory = undefined; return this; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, fireWith: function( context, args ) { if ( list && (!fired || stack) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, fire: function() { self.fireWith( this, arguments ); return this; } }, fired, firing, firingStart, firingLength, firingIndex, memory; return self; } function Deferred( func ) { var tuples = [ // action, add listener, listener list, final state [ 'resolve', 'done', Callbacks( true ), 'resolved' ], [ 'reject', 'fail', Callbacks( true ), 'rejected' ], [ 'notify', 'progress', Callbacks() ] ], state = 'pending', promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return Deferred(function( newDefer ) { $.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], fn = $.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for // forwarding actions to newDefer deferred[ tuple[ 1 ] ](function() { var returned; returned = fn && fn.apply( this, arguments ); if ( returned && $.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ action + 'With' ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? $.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods $.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + 'With' ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[ 0 ] + 'With' ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; } api = { /** * 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。 * 详细的Deferred用法说明,请参照jQuery的API文档。 * * Deferred对象在钩子回掉函数中经常要用到,用来处理需要等待的异步操作。 * * @for Base * @method Deferred * @grammar Base.Deferred() => Deferred * @example * // 在文件开始发送前做些异步操作。 * // WebUploader会等待此异步操作完成后,开始发送文件。 * Uploader.register({ * 'before-send-file': 'doSomthingAsync' * }, { * * doSomthingAsync: function() { * var deferred = Base.Deferred(); * * // 模拟一次异步操作。 * setTimeout(deferred.resolve, 2000); * * return deferred.promise(); * } * }); */ Deferred: Deferred, /** * 判断传入的参数是否为一个promise对象。 * @method isPromise * @grammar Base.isPromise( anything ) => Boolean * @param {*} anything 检测对象。 * @return {Boolean} * @for Base * @example * console.log( Base.isPromise() ); // => false * console.log( Base.isPromise({ key: '123' }) ); // => false * console.log( Base.isPromise( Base.Deferred().promise() ) ); // => true * * // Deferred也是一个Promise * console.log( Base.isPromise( Base.Deferred() ) ); // => true */ isPromise: function( anything ) { return anything && typeof anything.then === 'function'; }, /** * 返回一个promise,此promise在所有传入的promise都完成了后完成。 * 详细请查看[这里](http://api.jquery.com/jQuery.when/)。 * * @method when * @for Base * @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise */ when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, slice = [].slice, resolveValues = slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || (subordinate && $.isFunction( subordinate.promise )) ? length : 0, // the master Deferred. If resolveValues consist of // only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !(--remaining) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && $.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } }; return api; }); define('promise',[ 'promise-builtin' ], function( $ ) { return $; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 日志组件,主要用来收集错误信息,可以帮助 webuploader 更好的定位问题和发展。 * * 如果您不想要启用此功能,请在打包的时候去掉 log 模块。 * * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。 * * 如: * WebUploader.create({ * ... * * disableWidgets: 'log', * * ... * }) */ define('widgets/log',[ 'base', 'uploader', 'widgets/widget' ], function( Base, Uploader ) { var $ = Base.$, logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??', product = (location.hostname || location.host || 'protected').toLowerCase(), // 只针对 baidu 内部产品用户做统计功能。 enable = product && /baidu/i.exec(product), base; if (!enable) { return; } base = { dv: 3, master: 'webuploader', online: /test/.exec(product) ? 0 : 1, module: '', product: product, type: 0 }; function send(data) { var obj = $.extend({}, base, data), url = logUrl.replace(/^(.*)\?/, '$1' + $.param( obj )), image = new Image(); image.src = url; } return Uploader.register({ name: 'log', init: function() { var owner = this.owner, count = 0, size = 0; owner .on('error', function(code) { send({ type: 2, c_error_code: code }); }) .on('uploadError', function(file, reason) { send({ type: 2, c_error_code: 'UPLOAD_ERROR', c_reason: '' + reason }); }) .on('uploadComplete', function(file) { count++; size += file.size; }). on('uploadFinished', function() { send({ c_count: count, c_size: size }); count = size = 0; }); send({ c_usage: 1 }); } }); }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方式调用,比如android里面就能把context改成其他的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android不支持直接new Blob, 只能借助blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg. // 你得到的结果是png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * 代码来自于:https://github.com/blueimp/JavaScript-Load-Image * 暂时项目中只用了orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIF解析 */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // 尝试读取缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: 标记是否被修改过。 modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读取meta信息。 if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需要重新生成。 if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内存。非常重要,否则释放不了image的内存。 this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交换width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // 不允许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // 如果不是ios, 不需要这么复杂! if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * 这个方式性能不行,但是可以解决android里面的toDataUrl的bug * android里面toDataUrl('image/jpege')得到的结果却是png. * * 所以这里没辙,只能借助这个工具 * @fileOverview jpeg encoder */ define('runtime/html5/jpegencoder',[], function( require, exports, module ) { /* Copyright (c) 2008, Adobe Systems Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 Basic GUI blocking jpeg encoder */ function JPEGEncoder(quality) { var self = this; var fround = Math.round; var ffloor = Math.floor; var YTable = new Array(64); var UVTable = new Array(64); var fdtbl_Y = new Array(64); var fdtbl_UV = new Array(64); var YDC_HT; var UVDC_HT; var YAC_HT; var UVAC_HT; var bitcode = new Array(65535); var category = new Array(65535); var outputfDCTQuant = new Array(64); var DU = new Array(64); var byteout = []; var bytenew = 0; var bytepos = 7; var YDU = new Array(64); var UDU = new Array(64); var VDU = new Array(64); var clt = new Array(256); var RGB_YUV_TABLE = new Array(2048); var currentQuality; var ZigZag = [ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54, 20,22,33,38,46,51,55,60, 21,34,37,47,50,56,59,61, 35,36,48,49,57,58,62,63 ]; var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; var std_ac_luminance_values = [ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; var std_ac_chrominance_values = [ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; function initQuantTables(sf){ var YQT = [ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68,109,103, 77, 24, 35, 55, 64, 81,104,113, 92, 49, 64, 78, 87,103,121,120,101, 72, 92, 95, 98,112,100,103, 99 ]; for (var i = 0; i < 64; i++) { var t = ffloor((YQT[i]*sf+50)/100); if (t < 1) { t = 1; } else if (t > 255) { t = 255; } YTable[ZigZag[i]] = t; } var UVQT = [ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 ]; for (var j = 0; j < 64; j++) { var u = ffloor((UVQT[j]*sf+50)/100); if (u < 1) { u = 1; } else if (u > 255) { u = 255; } UVTable[ZigZag[j]] = u; } var aasf = [ 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 ]; var k = 0; for (var row = 0; row < 8; row++) { for (var col = 0; col < 8; col++) { fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); k++; } } } function computeHuffmanTbl(nrcodes, std_table){ var codevalue = 0; var pos_in_table = 0; var HT = new Array(); for (var k = 1; k <= 16; k++) { for (var j = 1; j <= nrcodes[k]; j++) { HT[std_table[pos_in_table]] = []; HT[std_table[pos_in_table]][0] = codevalue; HT[std_table[pos_in_table]][1] = k; pos_in_table++; codevalue++; } codevalue*=2; } return HT; } function initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); } function initCategoryNumber() { var nrlower = 1; var nrupper = 2; for (var cat = 1; cat <= 15; cat++) { //Positive numbers for (var nr = nrlower; nr>0] = 38470 * i; RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; } } // IO functions function writeBits(bs) { var value = bs[0]; var posval = bs[1]-1; while ( posval >= 0 ) { if (value & (1 << posval) ) { bytenew |= (1 << bytepos); } posval--; bytepos--; if (bytepos < 0) { if (bytenew == 0xFF) { writeByte(0xFF); writeByte(0); } else { writeByte(bytenew); } bytepos=7; bytenew=0; } } } function writeByte(value) { byteout.push(clt[value]); // write char directly instead of converting later } function writeWord(value) { writeByte((value>>8)&0xFF); writeByte((value )&0xFF); } // DCT & quantization core function fDCTQuant(data, fdtbl) { var d0, d1, d2, d3, d4, d5, d6, d7; /* Pass 1: process rows. */ var dataOff=0; var i; var I8 = 8; var I64 = 64; for (i=0; i 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); //outputfDCTQuant[i] = fround(fDCTQuant); } return outputfDCTQuant; } function writeAPP0() { writeWord(0xFFE0); // marker writeWord(16); // length writeByte(0x4A); // J writeByte(0x46); // F writeByte(0x49); // I writeByte(0x46); // F writeByte(0); // = "JFIF",'\0' writeByte(1); // versionhi writeByte(1); // versionlo writeByte(0); // xyunits writeWord(1); // xdensity writeWord(1); // ydensity writeByte(0); // thumbnwidth writeByte(0); // thumbnheight } function writeSOF0(width, height) { writeWord(0xFFC0); // marker writeWord(17); // length, truecolor YUV JPG writeByte(8); // precision writeWord(height); writeWord(width); writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0x11); // HVY writeByte(0); // QTY writeByte(2); // IdU writeByte(0x11); // HVU writeByte(1); // QTU writeByte(3); // IdV writeByte(0x11); // HVV writeByte(1); // QTV } function writeDQT() { writeWord(0xFFDB); // marker writeWord(132); // length writeByte(0); for (var i=0; i<64; i++) { writeByte(YTable[i]); } writeByte(1); for (var j=0; j<64; j++) { writeByte(UVTable[j]); } } function writeDHT() { writeWord(0xFFC4); // marker writeWord(0x01A2); // length writeByte(0); // HTYDCinfo for (var i=0; i<16; i++) { writeByte(std_dc_luminance_nrcodes[i+1]); } for (var j=0; j<=11; j++) { writeByte(std_dc_luminance_values[j]); } writeByte(0x10); // HTYACinfo for (var k=0; k<16; k++) { writeByte(std_ac_luminance_nrcodes[k+1]); } for (var l=0; l<=161; l++) { writeByte(std_ac_luminance_values[l]); } writeByte(1); // HTUDCinfo for (var m=0; m<16; m++) { writeByte(std_dc_chrominance_nrcodes[m+1]); } for (var n=0; n<=11; n++) { writeByte(std_dc_chrominance_values[n]); } writeByte(0x11); // HTUACinfo for (var o=0; o<16; o++) { writeByte(std_ac_chrominance_nrcodes[o+1]); } for (var p=0; p<=161; p++) { writeByte(std_ac_chrominance_values[p]); } } function writeSOS() { writeWord(0xFFDA); // marker writeWord(12); // length writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0); // HTY writeByte(2); // IdU writeByte(0x11); // HTU writeByte(3); // IdV writeByte(0x11); // HTV writeByte(0); // Ss writeByte(0x3f); // Se writeByte(0); // Bf } function processDU(CDU, fdtbl, DC, HTDC, HTAC){ var EOB = HTAC[0x00]; var M16zeroes = HTAC[0xF0]; var pos; var I16 = 16; var I63 = 63; var I64 = 64; var DU_DCT = fDCTQuant(CDU, fdtbl); //ZigZag reorder for (var j=0;j0)&&(DU[end0pos]==0); end0pos--) {}; //end0pos = first element in reverse order !=0 if ( end0pos == 0) { writeBits(EOB); return DC; } var i = 1; var lng; while ( i <= end0pos ) { var startpos = i; for (; (DU[i]==0) && (i<=end0pos); ++i) {} var nrzeroes = i-startpos; if ( nrzeroes >= I16 ) { lng = nrzeroes>>4; for (var nrmarker=1; nrmarker <= lng; ++nrmarker) writeBits(M16zeroes); nrzeroes = nrzeroes&0xF; } pos = 32767+DU[i]; writeBits(HTAC[(nrzeroes<<4)+category[pos]]); writeBits(bitcode[pos]); i++; } if ( end0pos != I63 ) { writeBits(EOB); } return DC; } function initCharLookupTable(){ var sfcc = String.fromCharCode; for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 clt[i] = sfcc(i); } } this.encode = function(image,quality) // image data object { // var time_start = new Date().getTime(); if(quality) setQuality(quality); // Initialize bit writer byteout = new Array(); bytenew=0; bytepos=7; // Add JPEG headers writeWord(0xFFD8); // SOI writeAPP0(); writeDQT(); writeSOF0(image.width,image.height); writeDHT(); writeSOS(); // Encode 8x8 macroblocks var DCY=0; var DCU=0; var DCV=0; bytenew=0; bytepos=7; this.encode.displayName = "_encode_"; var imageData = image.data; var width = image.width; var height = image.height; var quadWidth = width*4; var tripleWidth = width*3; var x, y = 0; var r, g, b; var start,p, col,row,pos; while(y < height){ x = 0; while(x < quadWidth){ start = quadWidth * y + x; p = start; col = -1; row = 0; for(pos=0; pos < 64; pos++){ row = pos >> 3;// /8 col = ( pos & 7 ) * 4; // %8 p = start + ( row * quadWidth ) + col; if(y+row >= height){ // padding bottom p-= (quadWidth*(y+1+row-height)); } if(x+col >= quadWidth){ // padding right p-= ((x+col) - quadWidth +4) } r = imageData[ p++ ]; g = imageData[ p++ ]; b = imageData[ p++ ]; /* // calculate YUV values dynamically YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); */ // use lookup table (slightly faster) YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; } DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); x+=32; } y+=8; } //////////////////////////////////////////////////////////////// // Do the bit alignment of the EOI marker if ( bytepos >= 0 ) { var fillbits = []; fillbits[1] = bytepos+1; fillbits[0] = (1<<(bytepos+1))-1; writeBits(fillbits); } writeWord(0xFFD9); //EOI var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); byteout = []; // benchmarking // var duration = new Date().getTime() - time_start; // console.log('Encoding time: '+ currentQuality + 'ms'); // return jpegDataUri } function setQuality(quality){ if (quality <= 0) { quality = 1; } if (quality > 100) { quality = 100; } if(currentQuality == quality) return // don't recalc if unchanged var sf = 0; if (quality < 50) { sf = Math.floor(5000 / quality); } else { sf = Math.floor(200 - quality*2); } initQuantTables(sf); currentQuality = quality; // console.log('Quality set to: '+quality +'%'); } function init(){ // var time_start = new Date().getTime(); if(!quality) quality = 50; // Create tables initCharLookupTable() initHuffmanTbl(); initCategoryNumber(); initRGBYUVTable(); setQuality(quality); // var duration = new Date().getTime() - time_start; // console.log('Initialization '+ duration + 'ms'); } init(); }; JPEGEncoder.encode = function( data, quality ) { var encoder = new JPEGEncoder( quality ); return encoder.encode( data ); } return JPEGEncoder; }); /** * @fileOverview Fix android canvas.toDataUrl bug. */ define('runtime/html5/androidpatch',[ 'runtime/html5/util', 'runtime/html5/jpegencoder', 'base' ], function( Util, encoder, Base ) { var origin = Util.canvasToDataUrl, supportJpeg; Util.canvasToDataUrl = function( canvas, type, quality ) { var ctx, w, h, fragement, parts; // 非android手机直接跳过。 if ( !Base.os.android ) { return origin.apply( null, arguments ); } // 检测是否canvas支持jpeg导出,根据数据格式来判断。 // JPEG 前两位分别是:255, 216 if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) { fragement = origin.apply( null, arguments ); parts = fragement.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { fragement = atob( parts[ 1 ] ); } else { fragement = decodeURIComponent( parts[ 1 ] ); } fragement = fragement.substring( 0, 2 ); supportJpeg = fragement.charCodeAt( 0 ) === 255 && fragement.charCodeAt( 1 ) === 216; } // 只有在android环境下才修复 if ( type === 'image/jpeg' && !supportJpeg ) { w = canvas.width; h = canvas.height; ctx = canvas.getContext('2d'); return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality ); } return origin.apply( null, arguments ); }; }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); define('webuploader',[ 'base', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/log', 'runtime/html5/blob', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/image', 'runtime/html5/androidpatch', 'runtime/html5/transport' ], function( Base ) { return Base; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.fis.js ================================================ /*! WebUploader 0.1.5 */ var jQuery = require('example:widget/ui/jquery/jquery.js') return (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }; return makeExport( jQuery ); })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Md5 */ define('lib/md5',[ 'runtime/client', 'mediator' ], function( RuntimeClient, Mediator ) { function Md5() { RuntimeClient.call( this, 'Md5' ); } // 让 Md5 具备事件功能。 Mediator.installTo( Md5.prototype ); Md5.prototype.loadFromBlob = function( blob ) { var me = this; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); me.exec( 'loadFromBlob', blob ); }); }; Md5.prototype.getResult = function() { return this.exec('getResult'); }; return Md5; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/md5',[ 'base', 'uploader', 'lib/md5', 'lib/blob', 'widgets/widget' ], function( Base, Uploader, Md5, Blob ) { return Uploader.register({ name: 'md5', /** * 计算文件 md5 值,返回一个 promise 对象,可以监听 progress 进度。 * * * @method md5File * @grammar md5File( file[, start[, end]] ) => promise * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.md5File( file ) * * // 及时显示进度 * .progress(function(percentage) { * console.log('Percentage:', percentage); * }) * * // 完成 * .then(function(val) { * console.log('md5 result:', val); * }); * * }); */ md5File: function( file, start, end ) { var md5 = new Md5(), deferred = Base.Deferred(), blob = (file instanceof Blob) ? file : this.request( 'get-file', file ).source; md5.on( 'progress load', function( e ) { e = e || {}; deferred.notify( e.total ? e.loaded / e.total : 1 ); }); md5.on( 'complete', function() { deferred.resolve( md5.getResult() ); }); md5.on( 'error', function( reason ) { deferred.reject( reason ); }); if ( arguments.length > 1 ) { start = start || 0; end = end || 0; start < 0 && (start = blob.size + start); end < 0 && (end = blob.size + end); end = Math.min( end, blob.size ); blob = blob.slice( start, end ); } md5.loadFromBlob( blob ); return deferred.promise(); } }); }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方式调用,比如android里面就能把context改成其他的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android不支持直接new Blob, 只能借助blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg. // 你得到的结果是png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * 代码来自于:https://github.com/blueimp/JavaScript-Load-Image * 暂时项目中只用了orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIF解析 */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // 尝试读取缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * 这个方式性能不行,但是可以解决android里面的toDataUrl的bug * android里面toDataUrl('image/jpege')得到的结果却是png. * * 所以这里没辙,只能借助这个工具 * @fileOverview jpeg encoder */ define('runtime/html5/jpegencoder',[], function( require, exports, module ) { /* Copyright (c) 2008, Adobe Systems Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 Basic GUI blocking jpeg encoder */ function JPEGEncoder(quality) { var self = this; var fround = Math.round; var ffloor = Math.floor; var YTable = new Array(64); var UVTable = new Array(64); var fdtbl_Y = new Array(64); var fdtbl_UV = new Array(64); var YDC_HT; var UVDC_HT; var YAC_HT; var UVAC_HT; var bitcode = new Array(65535); var category = new Array(65535); var outputfDCTQuant = new Array(64); var DU = new Array(64); var byteout = []; var bytenew = 0; var bytepos = 7; var YDU = new Array(64); var UDU = new Array(64); var VDU = new Array(64); var clt = new Array(256); var RGB_YUV_TABLE = new Array(2048); var currentQuality; var ZigZag = [ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54, 20,22,33,38,46,51,55,60, 21,34,37,47,50,56,59,61, 35,36,48,49,57,58,62,63 ]; var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; var std_ac_luminance_values = [ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; var std_ac_chrominance_values = [ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; function initQuantTables(sf){ var YQT = [ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68,109,103, 77, 24, 35, 55, 64, 81,104,113, 92, 49, 64, 78, 87,103,121,120,101, 72, 92, 95, 98,112,100,103, 99 ]; for (var i = 0; i < 64; i++) { var t = ffloor((YQT[i]*sf+50)/100); if (t < 1) { t = 1; } else if (t > 255) { t = 255; } YTable[ZigZag[i]] = t; } var UVQT = [ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 ]; for (var j = 0; j < 64; j++) { var u = ffloor((UVQT[j]*sf+50)/100); if (u < 1) { u = 1; } else if (u > 255) { u = 255; } UVTable[ZigZag[j]] = u; } var aasf = [ 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 ]; var k = 0; for (var row = 0; row < 8; row++) { for (var col = 0; col < 8; col++) { fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); k++; } } } function computeHuffmanTbl(nrcodes, std_table){ var codevalue = 0; var pos_in_table = 0; var HT = new Array(); for (var k = 1; k <= 16; k++) { for (var j = 1; j <= nrcodes[k]; j++) { HT[std_table[pos_in_table]] = []; HT[std_table[pos_in_table]][0] = codevalue; HT[std_table[pos_in_table]][1] = k; pos_in_table++; codevalue++; } codevalue*=2; } return HT; } function initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); } function initCategoryNumber() { var nrlower = 1; var nrupper = 2; for (var cat = 1; cat <= 15; cat++) { //Positive numbers for (var nr = nrlower; nr>0] = 38470 * i; RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; } } // IO functions function writeBits(bs) { var value = bs[0]; var posval = bs[1]-1; while ( posval >= 0 ) { if (value & (1 << posval) ) { bytenew |= (1 << bytepos); } posval--; bytepos--; if (bytepos < 0) { if (bytenew == 0xFF) { writeByte(0xFF); writeByte(0); } else { writeByte(bytenew); } bytepos=7; bytenew=0; } } } function writeByte(value) { byteout.push(clt[value]); // write char directly instead of converting later } function writeWord(value) { writeByte((value>>8)&0xFF); writeByte((value )&0xFF); } // DCT & quantization core function fDCTQuant(data, fdtbl) { var d0, d1, d2, d3, d4, d5, d6, d7; /* Pass 1: process rows. */ var dataOff=0; var i; var I8 = 8; var I64 = 64; for (i=0; i 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); //outputfDCTQuant[i] = fround(fDCTQuant); } return outputfDCTQuant; } function writeAPP0() { writeWord(0xFFE0); // marker writeWord(16); // length writeByte(0x4A); // J writeByte(0x46); // F writeByte(0x49); // I writeByte(0x46); // F writeByte(0); // = "JFIF",'\0' writeByte(1); // versionhi writeByte(1); // versionlo writeByte(0); // xyunits writeWord(1); // xdensity writeWord(1); // ydensity writeByte(0); // thumbnwidth writeByte(0); // thumbnheight } function writeSOF0(width, height) { writeWord(0xFFC0); // marker writeWord(17); // length, truecolor YUV JPG writeByte(8); // precision writeWord(height); writeWord(width); writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0x11); // HVY writeByte(0); // QTY writeByte(2); // IdU writeByte(0x11); // HVU writeByte(1); // QTU writeByte(3); // IdV writeByte(0x11); // HVV writeByte(1); // QTV } function writeDQT() { writeWord(0xFFDB); // marker writeWord(132); // length writeByte(0); for (var i=0; i<64; i++) { writeByte(YTable[i]); } writeByte(1); for (var j=0; j<64; j++) { writeByte(UVTable[j]); } } function writeDHT() { writeWord(0xFFC4); // marker writeWord(0x01A2); // length writeByte(0); // HTYDCinfo for (var i=0; i<16; i++) { writeByte(std_dc_luminance_nrcodes[i+1]); } for (var j=0; j<=11; j++) { writeByte(std_dc_luminance_values[j]); } writeByte(0x10); // HTYACinfo for (var k=0; k<16; k++) { writeByte(std_ac_luminance_nrcodes[k+1]); } for (var l=0; l<=161; l++) { writeByte(std_ac_luminance_values[l]); } writeByte(1); // HTUDCinfo for (var m=0; m<16; m++) { writeByte(std_dc_chrominance_nrcodes[m+1]); } for (var n=0; n<=11; n++) { writeByte(std_dc_chrominance_values[n]); } writeByte(0x11); // HTUACinfo for (var o=0; o<16; o++) { writeByte(std_ac_chrominance_nrcodes[o+1]); } for (var p=0; p<=161; p++) { writeByte(std_ac_chrominance_values[p]); } } function writeSOS() { writeWord(0xFFDA); // marker writeWord(12); // length writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0); // HTY writeByte(2); // IdU writeByte(0x11); // HTU writeByte(3); // IdV writeByte(0x11); // HTV writeByte(0); // Ss writeByte(0x3f); // Se writeByte(0); // Bf } function processDU(CDU, fdtbl, DC, HTDC, HTAC){ var EOB = HTAC[0x00]; var M16zeroes = HTAC[0xF0]; var pos; var I16 = 16; var I63 = 63; var I64 = 64; var DU_DCT = fDCTQuant(CDU, fdtbl); //ZigZag reorder for (var j=0;j0)&&(DU[end0pos]==0); end0pos--) {}; //end0pos = first element in reverse order !=0 if ( end0pos == 0) { writeBits(EOB); return DC; } var i = 1; var lng; while ( i <= end0pos ) { var startpos = i; for (; (DU[i]==0) && (i<=end0pos); ++i) {} var nrzeroes = i-startpos; if ( nrzeroes >= I16 ) { lng = nrzeroes>>4; for (var nrmarker=1; nrmarker <= lng; ++nrmarker) writeBits(M16zeroes); nrzeroes = nrzeroes&0xF; } pos = 32767+DU[i]; writeBits(HTAC[(nrzeroes<<4)+category[pos]]); writeBits(bitcode[pos]); i++; } if ( end0pos != I63 ) { writeBits(EOB); } return DC; } function initCharLookupTable(){ var sfcc = String.fromCharCode; for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 clt[i] = sfcc(i); } } this.encode = function(image,quality) // image data object { // var time_start = new Date().getTime(); if(quality) setQuality(quality); // Initialize bit writer byteout = new Array(); bytenew=0; bytepos=7; // Add JPEG headers writeWord(0xFFD8); // SOI writeAPP0(); writeDQT(); writeSOF0(image.width,image.height); writeDHT(); writeSOS(); // Encode 8x8 macroblocks var DCY=0; var DCU=0; var DCV=0; bytenew=0; bytepos=7; this.encode.displayName = "_encode_"; var imageData = image.data; var width = image.width; var height = image.height; var quadWidth = width*4; var tripleWidth = width*3; var x, y = 0; var r, g, b; var start,p, col,row,pos; while(y < height){ x = 0; while(x < quadWidth){ start = quadWidth * y + x; p = start; col = -1; row = 0; for(pos=0; pos < 64; pos++){ row = pos >> 3;// /8 col = ( pos & 7 ) * 4; // %8 p = start + ( row * quadWidth ) + col; if(y+row >= height){ // padding bottom p-= (quadWidth*(y+1+row-height)); } if(x+col >= quadWidth){ // padding right p-= ((x+col) - quadWidth +4) } r = imageData[ p++ ]; g = imageData[ p++ ]; b = imageData[ p++ ]; /* // calculate YUV values dynamically YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); */ // use lookup table (slightly faster) YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; } DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); x+=32; } y+=8; } //////////////////////////////////////////////////////////////// // Do the bit alignment of the EOI marker if ( bytepos >= 0 ) { var fillbits = []; fillbits[1] = bytepos+1; fillbits[0] = (1<<(bytepos+1))-1; writeBits(fillbits); } writeWord(0xFFD9); //EOI var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); byteout = []; // benchmarking // var duration = new Date().getTime() - time_start; // console.log('Encoding time: '+ currentQuality + 'ms'); // return jpegDataUri } function setQuality(quality){ if (quality <= 0) { quality = 1; } if (quality > 100) { quality = 100; } if(currentQuality == quality) return // don't recalc if unchanged var sf = 0; if (quality < 50) { sf = Math.floor(5000 / quality); } else { sf = Math.floor(200 - quality*2); } initQuantTables(sf); currentQuality = quality; // console.log('Quality set to: '+quality +'%'); } function init(){ // var time_start = new Date().getTime(); if(!quality) quality = 50; // Create tables initCharLookupTable() initHuffmanTbl(); initCategoryNumber(); initRGBYUVTable(); setQuality(quality); // var duration = new Date().getTime() - time_start; // console.log('Initialization '+ duration + 'ms'); } init(); }; JPEGEncoder.encode = function( data, quality ) { var encoder = new JPEGEncoder( quality ); return encoder.encode( data ); } return JPEGEncoder; }); /** * @fileOverview Fix android canvas.toDataUrl bug. */ define('runtime/html5/androidpatch',[ 'runtime/html5/util', 'runtime/html5/jpegencoder', 'base' ], function( Util, encoder, Base ) { var origin = Util.canvasToDataUrl, supportJpeg; Util.canvasToDataUrl = function( canvas, type, quality ) { var ctx, w, h, fragement, parts; // 非android手机直接跳过。 if ( !Base.os.android ) { return origin.apply( null, arguments ); } // 检测是否canvas支持jpeg导出,根据数据格式来判断。 // JPEG 前两位分别是:255, 216 if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) { fragement = origin.apply( null, arguments ); parts = fragement.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { fragement = atob( parts[ 1 ] ); } else { fragement = decodeURIComponent( parts[ 1 ] ); } fragement = fragement.substring( 0, 2 ); supportJpeg = fragement.charCodeAt( 0 ) === 255 && fragement.charCodeAt( 1 ) === 216; } // 只有在android环境下才修复 if ( type === 'image/jpeg' && !supportJpeg ) { w = canvas.width; h = canvas.height; ctx = canvas.getContext('2d'); return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality ); } return origin.apply( null, arguments ); }; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: 标记是否被修改过。 modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读取meta信息。 if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需要重新生成。 if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内存。非常重要,否则释放不了image的内存。 this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交换width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // 不允许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // 如果不是ios, 不需要这么复杂! if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/html5/md5',[ 'runtime/html5/runtime' ], function( FlashRuntime ) { /* * Fastest md5 implementation around (JKM md5) * Credits: Joseph Myers * * @see http://www.myersdaily.org/joseph/javascript/md5-text.html * @see http://jsperf.com/md5-shootout/7 */ /* this function is much faster, so if possible we use it. Some IEs are the only ones I know of that need the idiotic second function, generated by an if clause. */ var add32 = function (a, b) { return (a + b) & 0xFFFFFFFF; }, cmn = function (q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }, ff = function (a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }, gg = function (a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }, hh = function (a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }, ii = function (a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }, md5cycle = function (x, k) { var a = x[0], b = x[1], c = x[2], d = x[3]; a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); }, /* there needs to be support for Unicode here, * unless we pretend that we can redefine the MD-5 * algorithm for multi-byte characters (perhaps * by adding every four 16-bit characters and * shortening the sum to 32 bits). Otherwise * I suggest performing MD-5 as if every character * was two bytes--e.g., 0040 0025 = @%--but then * how will an ordinary MD-5 sum be matched? * There is no way to standardize text to something * like UTF-8 before transformation; speed cost is * utterly prohibitive. The JavaScript standard * itself needs to look at this: it should start * providing access to strings as preformed UTF-8 * 8-bit unsigned value arrays. */ md5blk = function (s) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } return md5blks; }, md5blk_array = function (a) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); } return md5blks; }, md51 = function (s) { var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } s = s.substring(i - 64); length = s.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, md51_array = function (a) { var n = a.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk_array(a.subarray(i - 64, i))); } // Not sure if it is a bug, however IE10 will always produce a sub array of length 1 // containing the last element of the parent array if the sub array specified starts // beyond the length of the parent array - weird. // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); length = a.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= a[i] << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], rhex = function (n) { var s = '', j; for (j = 0; j < 4; j += 1) { s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; } return s; }, hex = function (x) { var i; for (i = 0; i < x.length; i += 1) { x[i] = rhex(x[i]); } return x.join(''); }, md5 = function (s) { return hex(md51(s)); }, //////////////////////////////////////////////////////////////////////////// /** * SparkMD5 OOP implementation. * * Use this class to perform an incremental md5, otherwise use the * static methods instead. */ SparkMD5 = function () { // call reset to init the instance this.reset(); }; // In some cases the fast add32 function cannot be used.. if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') { add32 = function (x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF), msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); }; } /** * Appends a string. * A conversion will be applied if an utf8 string is detected. * * @param {String} str The string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.append = function (str) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } // then append as binary this.appendBinary(str); return this; }; /** * Appends a binary string. * * @param {String} contents The binary string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.appendBinary = function (contents) { this._buff += contents; this._length += contents.length; var length = this._buff.length, i; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk(this._buff.substring(i - 64, i))); } this._buff = this._buff.substr(i - 64); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.prototype.end = function (raw) { var buff = this._buff, length = buff.length, i, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; /** * Finish the final calculation based on the tail. * * @param {Array} tail The tail (will be modified) * @param {Number} length The length of the remaining buffer */ SparkMD5.prototype._finish = function (tail, length) { var i = length, tmp, lo, hi; tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(this._state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Do the final computation based on the tail and length // Beware that the final length may not fit in 32 bits so we take care of that tmp = this._length * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(this._state, tail); }; /** * Resets the internal state of the computation. * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.reset = function () { this._buff = ""; this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.prototype.destroy = function () { delete this._state; delete this._buff; delete this._length; }; /** * Performs the md5 hash on a string. * A conversion will be applied if utf8 string is detected. * * @param {String} str The string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hash = function (str, raw) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } var hash = md51(str); return !!raw ? hash : hex(hash); }; /** * Performs the md5 hash on a binary string. * * @param {String} content The binary string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hashBinary = function (content, raw) { var hash = md51(content); return !!raw ? hash : hex(hash); }; /** * SparkMD5 OOP implementation for array buffers. * * Use this class to perform an incremental md5 ONLY for array buffers. */ SparkMD5.ArrayBuffer = function () { // call reset to init the instance this.reset(); }; //////////////////////////////////////////////////////////////////////////// /** * Appends an array buffer. * * @param {ArrayBuffer} arr The array to be appended * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.append = function (arr) { // TODO: we could avoid the concatenation here but the algorithm would be more complex // if you find yourself needing extra performance, please make a PR. var buff = this._concatArrayBuffer(this._buff, arr), length = buff.length, i; this._length += arr.byteLength; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i))); } // Avoids IE10 weirdness (documented above) this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.prototype.end = function (raw) { var buff = this._buff, length = buff.length, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i, ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff[i] << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; /** * Resets the internal state of the computation. * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.reset = function () { this._buff = new Uint8Array(0); this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; /** * Concats two array buffers, returning a new one. * * @param {ArrayBuffer} first The first array buffer * @param {ArrayBuffer} second The second array buffer * * @return {ArrayBuffer} The new array buffer */ SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) { var firstLength = first.length, result = new Uint8Array(firstLength + second.byteLength); result.set(first); result.set(new Uint8Array(second), firstLength); return result; }; /** * Performs the md5 hash on an array buffer. * * @param {ArrayBuffer} arr The array buffer * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.hash = function (arr, raw) { var hash = md51_array(new Uint8Array(arr)); return !!raw ? hash : hex(hash); }; return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( file ) { var blob = file.getSource(), chunkSize = 2 * 1024 * 1024, chunks = Math.ceil( blob.size / chunkSize ), chunk = 0, owner = this.owner, spark = new SparkMD5.ArrayBuffer(), me = this, blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice, loadNext, fr; fr = new FileReader(); loadNext = function() { var start, end; start = chunk * chunkSize; end = Math.min( start + chunkSize, blob.size ); fr.onload = function( e ) { spark.append( e.target.result ); owner.trigger( 'progress', { total: file.size, loaded: end }); }; fr.onloadend = function() { fr.onloadend = fr.onload = null; if ( ++chunk < chunks ) { setTimeout( loadNext, 1 ); } else { setTimeout(function(){ owner.trigger('load'); me.result = spark.end(); loadNext = file = blob = spark = null; owner.trigger('complete'); }, 50 ); } }; fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) ); }; loadNext(); }, getResult: function() { return this.result; } }); }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview 图片压缩 */ define('runtime/flash/image',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Image', { // init: function( options ) { // var owner = this.owner; // this.flashExec( 'Image', 'init', options ); // owner.on( 'load', function() { // debugger; // }); // }, loadFromBlob: function( blob ) { var owner = this.owner; owner.info() && this.flashExec( 'Image', 'info', owner.info() ); owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() ); this.flashExec( 'Image', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview Md5 flash实现 */ define('runtime/flash/md5',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( blob ) { return this.flashExec( 'Md5', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview 完全版本。 */ define('preset/all',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', 'widgets/md5', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/androidpatch', 'runtime/html5/image', 'runtime/html5/transport', 'runtime/html5/md5', // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/transport', 'runtime/flash/blob', 'runtime/flash/md5' ], function( Base ) { return Base; }); /** * @fileOverview 日志组件,主要用来收集错误信息,可以帮助 webuploader 更好的定位问题和发展。 * * 如果您不想要启用此功能,请在打包的时候去掉 log 模块。 * * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。 * * 如: * WebUploader.create({ * ... * * disableWidgets: 'log', * * ... * }) */ define('widgets/log',[ 'base', 'uploader', 'widgets/widget' ], function( Base, Uploader ) { var $ = Base.$, logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??', product = (location.hostname || location.host || 'protected').toLowerCase(), // 只针对 baidu 内部产品用户做统计功能。 enable = product && /baidu/i.exec(product), base; if (!enable) { return; } base = { dv: 3, master: 'webuploader', online: /test/.exec(product) ? 0 : 1, module: '', product: product, type: 0 }; function send(data) { var obj = $.extend({}, base, data), url = logUrl.replace(/^(.*)\?/, '$1' + $.param( obj )), image = new Image(); image.src = url; } return Uploader.register({ name: 'log', init: function() { var owner = this.owner, count = 0, size = 0; owner .on('error', function(code) { send({ type: 2, c_error_code: code }); }) .on('uploadError', function(file, reason) { send({ type: 2, c_error_code: 'UPLOAD_ERROR', c_reason: '' + reason }); }) .on('uploadComplete', function(file) { count++; size += file.size; }). on('uploadFinished', function() { send({ c_count: count, c_size: size }); count = size = 0; }); send({ c_usage: 1 }); } }); }); /** * @fileOverview Uploader上传类 */ define('webuploader',[ 'preset/all', 'widgets/log' ], function( preset ) { return preset; }); var _require = require; return _require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.flashonly.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview 图片压缩 */ define('runtime/flash/image',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Image', { // init: function( options ) { // var owner = this.owner; // this.flashExec( 'Image', 'init', options ); // owner.on( 'load', function() { // debugger; // }); // }, loadFromBlob: function( blob ) { var owner = this.owner; owner.info() && this.flashExec( 'Image', 'info', owner.info() ); owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() ); this.flashExec( 'Image', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview 只有flash实现的文件版本。 */ define('preset/flashonly',[ 'base', // widgets 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', // runtimes // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/blob', 'runtime/flash/transport' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/flashonly' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.html5only.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方式调用,比如android里面就能把context改成其他的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android不支持直接new Blob, 只能借助blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg. // 你得到的结果是png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * 代码来自于:https://github.com/blueimp/JavaScript-Load-Image * 暂时项目中只用了orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIF解析 */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // 尝试读取缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: 标记是否被修改过。 modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读取meta信息。 if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需要重新生成。 if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内存。非常重要,否则释放不了image的内存。 this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交换width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // 不允许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // 如果不是ios, 不需要这么复杂! if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview 只有html5实现的文件版本。 */ define('preset/html5only',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/image', 'runtime/html5/transport' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/html5only' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Md5 */ define('lib/md5',[ 'runtime/client', 'mediator' ], function( RuntimeClient, Mediator ) { function Md5() { RuntimeClient.call( this, 'Md5' ); } // 让 Md5 具备事件功能。 Mediator.installTo( Md5.prototype ); Md5.prototype.loadFromBlob = function( blob ) { var me = this; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); me.exec( 'loadFromBlob', blob ); }); }; Md5.prototype.getResult = function() { return this.exec('getResult'); }; return Md5; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/md5',[ 'base', 'uploader', 'lib/md5', 'lib/blob', 'widgets/widget' ], function( Base, Uploader, Md5, Blob ) { return Uploader.register({ name: 'md5', /** * 计算文件 md5 值,返回一个 promise 对象,可以监听 progress 进度。 * * * @method md5File * @grammar md5File( file[, start[, end]] ) => promise * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.md5File( file ) * * // 及时显示进度 * .progress(function(percentage) { * console.log('Percentage:', percentage); * }) * * // 完成 * .then(function(val) { * console.log('md5 result:', val); * }); * * }); */ md5File: function( file, start, end ) { var md5 = new Md5(), deferred = Base.Deferred(), blob = (file instanceof Blob) ? file : this.request( 'get-file', file ).source; md5.on( 'progress load', function( e ) { e = e || {}; deferred.notify( e.total ? e.loaded / e.total : 1 ); }); md5.on( 'complete', function() { deferred.resolve( md5.getResult() ); }); md5.on( 'error', function( reason ) { deferred.reject( reason ); }); if ( arguments.length > 1 ) { start = start || 0; end = end || 0; start < 0 && (start = blob.size + start); end < 0 && (end = blob.size + end); end = Math.min( end, blob.size ); blob = blob.slice( start, end ); } md5.loadFromBlob( blob ); return deferred.promise(); } }); }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方式调用,比如android里面就能把context改成其他的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android不支持直接new Blob, 只能借助blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg. // 你得到的结果是png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * 代码来自于:https://github.com/blueimp/JavaScript-Load-Image * 暂时项目中只用了orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIF解析 */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // 尝试读取缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * 这个方式性能不行,但是可以解决android里面的toDataUrl的bug * android里面toDataUrl('image/jpege')得到的结果却是png. * * 所以这里没辙,只能借助这个工具 * @fileOverview jpeg encoder */ define('runtime/html5/jpegencoder',[], function( require, exports, module ) { /* Copyright (c) 2008, Adobe Systems Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 Basic GUI blocking jpeg encoder */ function JPEGEncoder(quality) { var self = this; var fround = Math.round; var ffloor = Math.floor; var YTable = new Array(64); var UVTable = new Array(64); var fdtbl_Y = new Array(64); var fdtbl_UV = new Array(64); var YDC_HT; var UVDC_HT; var YAC_HT; var UVAC_HT; var bitcode = new Array(65535); var category = new Array(65535); var outputfDCTQuant = new Array(64); var DU = new Array(64); var byteout = []; var bytenew = 0; var bytepos = 7; var YDU = new Array(64); var UDU = new Array(64); var VDU = new Array(64); var clt = new Array(256); var RGB_YUV_TABLE = new Array(2048); var currentQuality; var ZigZag = [ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54, 20,22,33,38,46,51,55,60, 21,34,37,47,50,56,59,61, 35,36,48,49,57,58,62,63 ]; var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; var std_ac_luminance_values = [ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; var std_ac_chrominance_values = [ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; function initQuantTables(sf){ var YQT = [ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68,109,103, 77, 24, 35, 55, 64, 81,104,113, 92, 49, 64, 78, 87,103,121,120,101, 72, 92, 95, 98,112,100,103, 99 ]; for (var i = 0; i < 64; i++) { var t = ffloor((YQT[i]*sf+50)/100); if (t < 1) { t = 1; } else if (t > 255) { t = 255; } YTable[ZigZag[i]] = t; } var UVQT = [ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 ]; for (var j = 0; j < 64; j++) { var u = ffloor((UVQT[j]*sf+50)/100); if (u < 1) { u = 1; } else if (u > 255) { u = 255; } UVTable[ZigZag[j]] = u; } var aasf = [ 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 ]; var k = 0; for (var row = 0; row < 8; row++) { for (var col = 0; col < 8; col++) { fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); k++; } } } function computeHuffmanTbl(nrcodes, std_table){ var codevalue = 0; var pos_in_table = 0; var HT = new Array(); for (var k = 1; k <= 16; k++) { for (var j = 1; j <= nrcodes[k]; j++) { HT[std_table[pos_in_table]] = []; HT[std_table[pos_in_table]][0] = codevalue; HT[std_table[pos_in_table]][1] = k; pos_in_table++; codevalue++; } codevalue*=2; } return HT; } function initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); } function initCategoryNumber() { var nrlower = 1; var nrupper = 2; for (var cat = 1; cat <= 15; cat++) { //Positive numbers for (var nr = nrlower; nr>0] = 38470 * i; RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; } } // IO functions function writeBits(bs) { var value = bs[0]; var posval = bs[1]-1; while ( posval >= 0 ) { if (value & (1 << posval) ) { bytenew |= (1 << bytepos); } posval--; bytepos--; if (bytepos < 0) { if (bytenew == 0xFF) { writeByte(0xFF); writeByte(0); } else { writeByte(bytenew); } bytepos=7; bytenew=0; } } } function writeByte(value) { byteout.push(clt[value]); // write char directly instead of converting later } function writeWord(value) { writeByte((value>>8)&0xFF); writeByte((value )&0xFF); } // DCT & quantization core function fDCTQuant(data, fdtbl) { var d0, d1, d2, d3, d4, d5, d6, d7; /* Pass 1: process rows. */ var dataOff=0; var i; var I8 = 8; var I64 = 64; for (i=0; i 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); //outputfDCTQuant[i] = fround(fDCTQuant); } return outputfDCTQuant; } function writeAPP0() { writeWord(0xFFE0); // marker writeWord(16); // length writeByte(0x4A); // J writeByte(0x46); // F writeByte(0x49); // I writeByte(0x46); // F writeByte(0); // = "JFIF",'\0' writeByte(1); // versionhi writeByte(1); // versionlo writeByte(0); // xyunits writeWord(1); // xdensity writeWord(1); // ydensity writeByte(0); // thumbnwidth writeByte(0); // thumbnheight } function writeSOF0(width, height) { writeWord(0xFFC0); // marker writeWord(17); // length, truecolor YUV JPG writeByte(8); // precision writeWord(height); writeWord(width); writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0x11); // HVY writeByte(0); // QTY writeByte(2); // IdU writeByte(0x11); // HVU writeByte(1); // QTU writeByte(3); // IdV writeByte(0x11); // HVV writeByte(1); // QTV } function writeDQT() { writeWord(0xFFDB); // marker writeWord(132); // length writeByte(0); for (var i=0; i<64; i++) { writeByte(YTable[i]); } writeByte(1); for (var j=0; j<64; j++) { writeByte(UVTable[j]); } } function writeDHT() { writeWord(0xFFC4); // marker writeWord(0x01A2); // length writeByte(0); // HTYDCinfo for (var i=0; i<16; i++) { writeByte(std_dc_luminance_nrcodes[i+1]); } for (var j=0; j<=11; j++) { writeByte(std_dc_luminance_values[j]); } writeByte(0x10); // HTYACinfo for (var k=0; k<16; k++) { writeByte(std_ac_luminance_nrcodes[k+1]); } for (var l=0; l<=161; l++) { writeByte(std_ac_luminance_values[l]); } writeByte(1); // HTUDCinfo for (var m=0; m<16; m++) { writeByte(std_dc_chrominance_nrcodes[m+1]); } for (var n=0; n<=11; n++) { writeByte(std_dc_chrominance_values[n]); } writeByte(0x11); // HTUACinfo for (var o=0; o<16; o++) { writeByte(std_ac_chrominance_nrcodes[o+1]); } for (var p=0; p<=161; p++) { writeByte(std_ac_chrominance_values[p]); } } function writeSOS() { writeWord(0xFFDA); // marker writeWord(12); // length writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0); // HTY writeByte(2); // IdU writeByte(0x11); // HTU writeByte(3); // IdV writeByte(0x11); // HTV writeByte(0); // Ss writeByte(0x3f); // Se writeByte(0); // Bf } function processDU(CDU, fdtbl, DC, HTDC, HTAC){ var EOB = HTAC[0x00]; var M16zeroes = HTAC[0xF0]; var pos; var I16 = 16; var I63 = 63; var I64 = 64; var DU_DCT = fDCTQuant(CDU, fdtbl); //ZigZag reorder for (var j=0;j0)&&(DU[end0pos]==0); end0pos--) {}; //end0pos = first element in reverse order !=0 if ( end0pos == 0) { writeBits(EOB); return DC; } var i = 1; var lng; while ( i <= end0pos ) { var startpos = i; for (; (DU[i]==0) && (i<=end0pos); ++i) {} var nrzeroes = i-startpos; if ( nrzeroes >= I16 ) { lng = nrzeroes>>4; for (var nrmarker=1; nrmarker <= lng; ++nrmarker) writeBits(M16zeroes); nrzeroes = nrzeroes&0xF; } pos = 32767+DU[i]; writeBits(HTAC[(nrzeroes<<4)+category[pos]]); writeBits(bitcode[pos]); i++; } if ( end0pos != I63 ) { writeBits(EOB); } return DC; } function initCharLookupTable(){ var sfcc = String.fromCharCode; for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 clt[i] = sfcc(i); } } this.encode = function(image,quality) // image data object { // var time_start = new Date().getTime(); if(quality) setQuality(quality); // Initialize bit writer byteout = new Array(); bytenew=0; bytepos=7; // Add JPEG headers writeWord(0xFFD8); // SOI writeAPP0(); writeDQT(); writeSOF0(image.width,image.height); writeDHT(); writeSOS(); // Encode 8x8 macroblocks var DCY=0; var DCU=0; var DCV=0; bytenew=0; bytepos=7; this.encode.displayName = "_encode_"; var imageData = image.data; var width = image.width; var height = image.height; var quadWidth = width*4; var tripleWidth = width*3; var x, y = 0; var r, g, b; var start,p, col,row,pos; while(y < height){ x = 0; while(x < quadWidth){ start = quadWidth * y + x; p = start; col = -1; row = 0; for(pos=0; pos < 64; pos++){ row = pos >> 3;// /8 col = ( pos & 7 ) * 4; // %8 p = start + ( row * quadWidth ) + col; if(y+row >= height){ // padding bottom p-= (quadWidth*(y+1+row-height)); } if(x+col >= quadWidth){ // padding right p-= ((x+col) - quadWidth +4) } r = imageData[ p++ ]; g = imageData[ p++ ]; b = imageData[ p++ ]; /* // calculate YUV values dynamically YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); */ // use lookup table (slightly faster) YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; } DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); x+=32; } y+=8; } //////////////////////////////////////////////////////////////// // Do the bit alignment of the EOI marker if ( bytepos >= 0 ) { var fillbits = []; fillbits[1] = bytepos+1; fillbits[0] = (1<<(bytepos+1))-1; writeBits(fillbits); } writeWord(0xFFD9); //EOI var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); byteout = []; // benchmarking // var duration = new Date().getTime() - time_start; // console.log('Encoding time: '+ currentQuality + 'ms'); // return jpegDataUri } function setQuality(quality){ if (quality <= 0) { quality = 1; } if (quality > 100) { quality = 100; } if(currentQuality == quality) return // don't recalc if unchanged var sf = 0; if (quality < 50) { sf = Math.floor(5000 / quality); } else { sf = Math.floor(200 - quality*2); } initQuantTables(sf); currentQuality = quality; // console.log('Quality set to: '+quality +'%'); } function init(){ // var time_start = new Date().getTime(); if(!quality) quality = 50; // Create tables initCharLookupTable() initHuffmanTbl(); initCategoryNumber(); initRGBYUVTable(); setQuality(quality); // var duration = new Date().getTime() - time_start; // console.log('Initialization '+ duration + 'ms'); } init(); }; JPEGEncoder.encode = function( data, quality ) { var encoder = new JPEGEncoder( quality ); return encoder.encode( data ); } return JPEGEncoder; }); /** * @fileOverview Fix android canvas.toDataUrl bug. */ define('runtime/html5/androidpatch',[ 'runtime/html5/util', 'runtime/html5/jpegencoder', 'base' ], function( Util, encoder, Base ) { var origin = Util.canvasToDataUrl, supportJpeg; Util.canvasToDataUrl = function( canvas, type, quality ) { var ctx, w, h, fragement, parts; // 非android手机直接跳过。 if ( !Base.os.android ) { return origin.apply( null, arguments ); } // 检测是否canvas支持jpeg导出,根据数据格式来判断。 // JPEG 前两位分别是:255, 216 if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) { fragement = origin.apply( null, arguments ); parts = fragement.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { fragement = atob( parts[ 1 ] ); } else { fragement = decodeURIComponent( parts[ 1 ] ); } fragement = fragement.substring( 0, 2 ); supportJpeg = fragement.charCodeAt( 0 ) === 255 && fragement.charCodeAt( 1 ) === 216; } // 只有在android环境下才修复 if ( type === 'image/jpeg' && !supportJpeg ) { w = canvas.width; h = canvas.height; ctx = canvas.getContext('2d'); return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality ); } return origin.apply( null, arguments ); }; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: 标记是否被修改过。 modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读取meta信息。 if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需要重新生成。 if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内存。非常重要,否则释放不了image的内存。 this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交换width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // 不允许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // 如果不是ios, 不需要这么复杂! if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/html5/md5',[ 'runtime/html5/runtime' ], function( FlashRuntime ) { /* * Fastest md5 implementation around (JKM md5) * Credits: Joseph Myers * * @see http://www.myersdaily.org/joseph/javascript/md5-text.html * @see http://jsperf.com/md5-shootout/7 */ /* this function is much faster, so if possible we use it. Some IEs are the only ones I know of that need the idiotic second function, generated by an if clause. */ var add32 = function (a, b) { return (a + b) & 0xFFFFFFFF; }, cmn = function (q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }, ff = function (a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }, gg = function (a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }, hh = function (a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }, ii = function (a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }, md5cycle = function (x, k) { var a = x[0], b = x[1], c = x[2], d = x[3]; a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); }, /* there needs to be support for Unicode here, * unless we pretend that we can redefine the MD-5 * algorithm for multi-byte characters (perhaps * by adding every four 16-bit characters and * shortening the sum to 32 bits). Otherwise * I suggest performing MD-5 as if every character * was two bytes--e.g., 0040 0025 = @%--but then * how will an ordinary MD-5 sum be matched? * There is no way to standardize text to something * like UTF-8 before transformation; speed cost is * utterly prohibitive. The JavaScript standard * itself needs to look at this: it should start * providing access to strings as preformed UTF-8 * 8-bit unsigned value arrays. */ md5blk = function (s) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } return md5blks; }, md5blk_array = function (a) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); } return md5blks; }, md51 = function (s) { var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } s = s.substring(i - 64); length = s.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, md51_array = function (a) { var n = a.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk_array(a.subarray(i - 64, i))); } // Not sure if it is a bug, however IE10 will always produce a sub array of length 1 // containing the last element of the parent array if the sub array specified starts // beyond the length of the parent array - weird. // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); length = a.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= a[i] << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], rhex = function (n) { var s = '', j; for (j = 0; j < 4; j += 1) { s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; } return s; }, hex = function (x) { var i; for (i = 0; i < x.length; i += 1) { x[i] = rhex(x[i]); } return x.join(''); }, md5 = function (s) { return hex(md51(s)); }, //////////////////////////////////////////////////////////////////////////// /** * SparkMD5 OOP implementation. * * Use this class to perform an incremental md5, otherwise use the * static methods instead. */ SparkMD5 = function () { // call reset to init the instance this.reset(); }; // In some cases the fast add32 function cannot be used.. if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') { add32 = function (x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF), msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); }; } /** * Appends a string. * A conversion will be applied if an utf8 string is detected. * * @param {String} str The string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.append = function (str) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } // then append as binary this.appendBinary(str); return this; }; /** * Appends a binary string. * * @param {String} contents The binary string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.appendBinary = function (contents) { this._buff += contents; this._length += contents.length; var length = this._buff.length, i; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk(this._buff.substring(i - 64, i))); } this._buff = this._buff.substr(i - 64); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.prototype.end = function (raw) { var buff = this._buff, length = buff.length, i, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; /** * Finish the final calculation based on the tail. * * @param {Array} tail The tail (will be modified) * @param {Number} length The length of the remaining buffer */ SparkMD5.prototype._finish = function (tail, length) { var i = length, tmp, lo, hi; tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(this._state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Do the final computation based on the tail and length // Beware that the final length may not fit in 32 bits so we take care of that tmp = this._length * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(this._state, tail); }; /** * Resets the internal state of the computation. * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.reset = function () { this._buff = ""; this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.prototype.destroy = function () { delete this._state; delete this._buff; delete this._length; }; /** * Performs the md5 hash on a string. * A conversion will be applied if utf8 string is detected. * * @param {String} str The string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hash = function (str, raw) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } var hash = md51(str); return !!raw ? hash : hex(hash); }; /** * Performs the md5 hash on a binary string. * * @param {String} content The binary string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hashBinary = function (content, raw) { var hash = md51(content); return !!raw ? hash : hex(hash); }; /** * SparkMD5 OOP implementation for array buffers. * * Use this class to perform an incremental md5 ONLY for array buffers. */ SparkMD5.ArrayBuffer = function () { // call reset to init the instance this.reset(); }; //////////////////////////////////////////////////////////////////////////// /** * Appends an array buffer. * * @param {ArrayBuffer} arr The array to be appended * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.append = function (arr) { // TODO: we could avoid the concatenation here but the algorithm would be more complex // if you find yourself needing extra performance, please make a PR. var buff = this._concatArrayBuffer(this._buff, arr), length = buff.length, i; this._length += arr.byteLength; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i))); } // Avoids IE10 weirdness (documented above) this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.prototype.end = function (raw) { var buff = this._buff, length = buff.length, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i, ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff[i] << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; /** * Resets the internal state of the computation. * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.reset = function () { this._buff = new Uint8Array(0); this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; /** * Concats two array buffers, returning a new one. * * @param {ArrayBuffer} first The first array buffer * @param {ArrayBuffer} second The second array buffer * * @return {ArrayBuffer} The new array buffer */ SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) { var firstLength = first.length, result = new Uint8Array(firstLength + second.byteLength); result.set(first); result.set(new Uint8Array(second), firstLength); return result; }; /** * Performs the md5 hash on an array buffer. * * @param {ArrayBuffer} arr The array buffer * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.hash = function (arr, raw) { var hash = md51_array(new Uint8Array(arr)); return !!raw ? hash : hex(hash); }; return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( file ) { var blob = file.getSource(), chunkSize = 2 * 1024 * 1024, chunks = Math.ceil( blob.size / chunkSize ), chunk = 0, owner = this.owner, spark = new SparkMD5.ArrayBuffer(), me = this, blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice, loadNext, fr; fr = new FileReader(); loadNext = function() { var start, end; start = chunk * chunkSize; end = Math.min( start + chunkSize, blob.size ); fr.onload = function( e ) { spark.append( e.target.result ); owner.trigger( 'progress', { total: file.size, loaded: end }); }; fr.onloadend = function() { fr.onloadend = fr.onload = null; if ( ++chunk < chunks ) { setTimeout( loadNext, 1 ); } else { setTimeout(function(){ owner.trigger('load'); me.result = spark.end(); loadNext = file = blob = spark = null; owner.trigger('complete'); }, 50 ); } }; fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) ); }; loadNext(); }, getResult: function() { return this.result; } }); }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview 图片压缩 */ define('runtime/flash/image',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Image', { // init: function( options ) { // var owner = this.owner; // this.flashExec( 'Image', 'init', options ); // owner.on( 'load', function() { // debugger; // }); // }, loadFromBlob: function( blob ) { var owner = this.owner; owner.info() && this.flashExec( 'Image', 'info', owner.info() ); owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() ); this.flashExec( 'Image', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview Md5 flash实现 */ define('runtime/flash/md5',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( blob ) { return this.flashExec( 'Md5', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview 完全版本。 */ define('preset/all',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', 'widgets/md5', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/androidpatch', 'runtime/html5/image', 'runtime/html5/transport', 'runtime/html5/md5', // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/transport', 'runtime/flash/blob', 'runtime/flash/md5' ], function( Base ) { return Base; }); /** * @fileOverview 日志组件,主要用来收集错误信息,可以帮助 webuploader 更好的定位问题和发展。 * * 如果您不想要启用此功能,请在打包的时候去掉 log 模块。 * * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。 * * 如: * WebUploader.create({ * ... * * disableWidgets: 'log', * * ... * }) */ define('widgets/log',[ 'base', 'uploader', 'widgets/widget' ], function( Base, Uploader ) { var $ = Base.$, logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??', product = (location.hostname || location.host || 'protected').toLowerCase(), // 只针对 baidu 内部产品用户做统计功能。 enable = product && /baidu/i.exec(product), base; if (!enable) { return; } base = { dv: 3, master: 'webuploader', online: /test/.exec(product) ? 0 : 1, module: '', product: product, type: 0 }; function send(data) { var obj = $.extend({}, base, data), url = logUrl.replace(/^(.*)\?/, '$1' + $.param( obj )), image = new Image(); image.src = url; } return Uploader.register({ name: 'log', init: function() { var owner = this.owner, count = 0, size = 0; owner .on('error', function(code) { send({ type: 2, c_error_code: code }); }) .on('uploadError', function(file, reason) { send({ type: 2, c_error_code: 'UPLOAD_ERROR', c_reason: '' + reason }); }) .on('uploadComplete', function(file) { count++; size += file.size; }). on('uploadFinished', function() { send({ c_count: count, c_size: size }); count = size = 0; }); send({ c_usage: 1 }); } }); }); /** * @fileOverview Uploader上传类 */ define('webuploader',[ 'preset/all', 'widgets/log' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.noimage.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview 没有图像处理的版本。 */ define('preset/withoutimage',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/transport', // flash 'runtime/flash/filepicker', 'runtime/flash/transport', 'runtime/flash/blob' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/withoutimage' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.nolog.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // 构造器。 function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处理质量 quality: 90, // 是否裁剪 crop: false, // 是否保留头部信息 preserveHeaders: false, // 是否允许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // 根据要处理的文件大小来节流,一次不能处理太多,会卡。 throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description 配置生成缩略图的选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 70, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: true, * * // 是否允许裁剪。 * crop: true, * * // 为空的话则保留原有图片格式。 * // 否则强制转换成指定的类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的话则保留原有图片格式。 // 否则强制转换成指定的类型。 // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可 // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。 * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质量,只有type为`image/jpeg`的时候才有效。 * quality: 90, * * // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. * allowMagnify: false, * * // 是否允许裁剪。 * crop: false, * * // 是否保留头部meta信息。 * preserveHeaders: true, * * // 如果发现压缩后文件大小比原来还大,则使用原来图片 * // 此属性可能会影响图片自动纠正功能 * noCompressIfLarger: false, * * // 单位字节,如果图片大小小于此值,不会采用压缩。 * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生成缩略图,此过程为异步,所以需要传入`callback`。 * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当成百分比使用。 * * `callback`中可以接收到两个参数。 * * 第一个为error,如果生成缩略图有错误,此error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注意** * Date URL在IE6/7中不支持,所以不用调用此方法了,直接显示一张暂不支持预览图片好了。 * 也可以借助服务端,将 base64 数据传给服务端,生成一个临时文件供预览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append(''); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // 只预览图片格式。 if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // 如果传入的是object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize 完后 image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // 只压缩 jpeg 图片格式。 // gif 可能会丢失针 // bmp png 基本上尺寸都不大,且压缩比比较小。 if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // 同样的规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq 浏览器的无图模式下 // ctx.getImageData 处理大图的时候会报 Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩后,比原来还大则不用压缩后的。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // 标记,避免重复压缩。 file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继续,让其上传原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定运行时启动顺序。默认会想尝试 html5 是否支持,如果支持则使用 html5, 否则则使用 flash. * * 可以将此值设置成 `flash`,来强制使用 flash 运行时。 */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Md5 */ define('lib/md5',[ 'runtime/client', 'mediator' ], function( RuntimeClient, Mediator ) { function Md5() { RuntimeClient.call( this, 'Md5' ); } // 让 Md5 具备事件功能。 Mediator.installTo( Md5.prototype ); Md5.prototype.loadFromBlob = function( blob ) { var me = this; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); me.exec( 'loadFromBlob', blob ); }); }; Md5.prototype.getResult = function() { return this.exec('getResult'); }; return Md5; }); /** * @fileOverview 图片操作, 负责预览图片和上传前压缩图片 */ define('widgets/md5',[ 'base', 'uploader', 'lib/md5', 'lib/blob', 'widgets/widget' ], function( Base, Uploader, Md5, Blob ) { return Uploader.register({ name: 'md5', /** * 计算文件 md5 值,返回一个 promise 对象,可以监听 progress 进度。 * * * @method md5File * @grammar md5File( file[, start[, end]] ) => promise * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.md5File( file ) * * // 及时显示进度 * .progress(function(percentage) { * console.log('Percentage:', percentage); * }) * * // 完成 * .then(function(val) { * console.log('md5 result:', val); * }); * * }); */ md5File: function( file, start, end ) { var md5 = new Md5(), deferred = Base.Deferred(), blob = (file instanceof Blob) ? file : this.request( 'get-file', file ).source; md5.on( 'progress load', function( e ) { e = e || {}; deferred.notify( e.total ? e.loaded / e.total : 1 ); }); md5.on( 'complete', function() { deferred.resolve( md5.getResult() ); }); md5.on( 'error', function( reason ) { deferred.reject( reason ); }); if ( arguments.length > 1 ) { start = start || 0; end = end || 0; start < 0 && (start = blob.size + start); end < 0 && (end = blob.size + end); end = Math.min( end, blob.size ); blob = blob.slice( start, end ); } md5.loadFromBlob( blob ); return deferred.promise(); } }); }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方式调用,比如android里面就能把context改成其他的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android不支持直接new Blob, 只能借助blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg. // 你得到的结果是png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会复写这个方法,如果用户选择加载那个文件了的话。 updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * 代码来自于:https://github.com/blueimp/JavaScript-Load-Image * 暂时项目中只用了orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIF解析 */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // 尝试读取缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * 这个方式性能不行,但是可以解决android里面的toDataUrl的bug * android里面toDataUrl('image/jpege')得到的结果却是png. * * 所以这里没辙,只能借助这个工具 * @fileOverview jpeg encoder */ define('runtime/html5/jpegencoder',[], function( require, exports, module ) { /* Copyright (c) 2008, Adobe Systems Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 Basic GUI blocking jpeg encoder */ function JPEGEncoder(quality) { var self = this; var fround = Math.round; var ffloor = Math.floor; var YTable = new Array(64); var UVTable = new Array(64); var fdtbl_Y = new Array(64); var fdtbl_UV = new Array(64); var YDC_HT; var UVDC_HT; var YAC_HT; var UVAC_HT; var bitcode = new Array(65535); var category = new Array(65535); var outputfDCTQuant = new Array(64); var DU = new Array(64); var byteout = []; var bytenew = 0; var bytepos = 7; var YDU = new Array(64); var UDU = new Array(64); var VDU = new Array(64); var clt = new Array(256); var RGB_YUV_TABLE = new Array(2048); var currentQuality; var ZigZag = [ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54, 20,22,33,38,46,51,55,60, 21,34,37,47,50,56,59,61, 35,36,48,49,57,58,62,63 ]; var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; var std_ac_luminance_values = [ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; var std_ac_chrominance_values = [ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; function initQuantTables(sf){ var YQT = [ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68,109,103, 77, 24, 35, 55, 64, 81,104,113, 92, 49, 64, 78, 87,103,121,120,101, 72, 92, 95, 98,112,100,103, 99 ]; for (var i = 0; i < 64; i++) { var t = ffloor((YQT[i]*sf+50)/100); if (t < 1) { t = 1; } else if (t > 255) { t = 255; } YTable[ZigZag[i]] = t; } var UVQT = [ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 ]; for (var j = 0; j < 64; j++) { var u = ffloor((UVQT[j]*sf+50)/100); if (u < 1) { u = 1; } else if (u > 255) { u = 255; } UVTable[ZigZag[j]] = u; } var aasf = [ 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 ]; var k = 0; for (var row = 0; row < 8; row++) { for (var col = 0; col < 8; col++) { fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); k++; } } } function computeHuffmanTbl(nrcodes, std_table){ var codevalue = 0; var pos_in_table = 0; var HT = new Array(); for (var k = 1; k <= 16; k++) { for (var j = 1; j <= nrcodes[k]; j++) { HT[std_table[pos_in_table]] = []; HT[std_table[pos_in_table]][0] = codevalue; HT[std_table[pos_in_table]][1] = k; pos_in_table++; codevalue++; } codevalue*=2; } return HT; } function initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); } function initCategoryNumber() { var nrlower = 1; var nrupper = 2; for (var cat = 1; cat <= 15; cat++) { //Positive numbers for (var nr = nrlower; nr>0] = 38470 * i; RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; } } // IO functions function writeBits(bs) { var value = bs[0]; var posval = bs[1]-1; while ( posval >= 0 ) { if (value & (1 << posval) ) { bytenew |= (1 << bytepos); } posval--; bytepos--; if (bytepos < 0) { if (bytenew == 0xFF) { writeByte(0xFF); writeByte(0); } else { writeByte(bytenew); } bytepos=7; bytenew=0; } } } function writeByte(value) { byteout.push(clt[value]); // write char directly instead of converting later } function writeWord(value) { writeByte((value>>8)&0xFF); writeByte((value )&0xFF); } // DCT & quantization core function fDCTQuant(data, fdtbl) { var d0, d1, d2, d3, d4, d5, d6, d7; /* Pass 1: process rows. */ var dataOff=0; var i; var I8 = 8; var I64 = 64; for (i=0; i 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); //outputfDCTQuant[i] = fround(fDCTQuant); } return outputfDCTQuant; } function writeAPP0() { writeWord(0xFFE0); // marker writeWord(16); // length writeByte(0x4A); // J writeByte(0x46); // F writeByte(0x49); // I writeByte(0x46); // F writeByte(0); // = "JFIF",'\0' writeByte(1); // versionhi writeByte(1); // versionlo writeByte(0); // xyunits writeWord(1); // xdensity writeWord(1); // ydensity writeByte(0); // thumbnwidth writeByte(0); // thumbnheight } function writeSOF0(width, height) { writeWord(0xFFC0); // marker writeWord(17); // length, truecolor YUV JPG writeByte(8); // precision writeWord(height); writeWord(width); writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0x11); // HVY writeByte(0); // QTY writeByte(2); // IdU writeByte(0x11); // HVU writeByte(1); // QTU writeByte(3); // IdV writeByte(0x11); // HVV writeByte(1); // QTV } function writeDQT() { writeWord(0xFFDB); // marker writeWord(132); // length writeByte(0); for (var i=0; i<64; i++) { writeByte(YTable[i]); } writeByte(1); for (var j=0; j<64; j++) { writeByte(UVTable[j]); } } function writeDHT() { writeWord(0xFFC4); // marker writeWord(0x01A2); // length writeByte(0); // HTYDCinfo for (var i=0; i<16; i++) { writeByte(std_dc_luminance_nrcodes[i+1]); } for (var j=0; j<=11; j++) { writeByte(std_dc_luminance_values[j]); } writeByte(0x10); // HTYACinfo for (var k=0; k<16; k++) { writeByte(std_ac_luminance_nrcodes[k+1]); } for (var l=0; l<=161; l++) { writeByte(std_ac_luminance_values[l]); } writeByte(1); // HTUDCinfo for (var m=0; m<16; m++) { writeByte(std_dc_chrominance_nrcodes[m+1]); } for (var n=0; n<=11; n++) { writeByte(std_dc_chrominance_values[n]); } writeByte(0x11); // HTUACinfo for (var o=0; o<16; o++) { writeByte(std_ac_chrominance_nrcodes[o+1]); } for (var p=0; p<=161; p++) { writeByte(std_ac_chrominance_values[p]); } } function writeSOS() { writeWord(0xFFDA); // marker writeWord(12); // length writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0); // HTY writeByte(2); // IdU writeByte(0x11); // HTU writeByte(3); // IdV writeByte(0x11); // HTV writeByte(0); // Ss writeByte(0x3f); // Se writeByte(0); // Bf } function processDU(CDU, fdtbl, DC, HTDC, HTAC){ var EOB = HTAC[0x00]; var M16zeroes = HTAC[0xF0]; var pos; var I16 = 16; var I63 = 63; var I64 = 64; var DU_DCT = fDCTQuant(CDU, fdtbl); //ZigZag reorder for (var j=0;j0)&&(DU[end0pos]==0); end0pos--) {}; //end0pos = first element in reverse order !=0 if ( end0pos == 0) { writeBits(EOB); return DC; } var i = 1; var lng; while ( i <= end0pos ) { var startpos = i; for (; (DU[i]==0) && (i<=end0pos); ++i) {} var nrzeroes = i-startpos; if ( nrzeroes >= I16 ) { lng = nrzeroes>>4; for (var nrmarker=1; nrmarker <= lng; ++nrmarker) writeBits(M16zeroes); nrzeroes = nrzeroes&0xF; } pos = 32767+DU[i]; writeBits(HTAC[(nrzeroes<<4)+category[pos]]); writeBits(bitcode[pos]); i++; } if ( end0pos != I63 ) { writeBits(EOB); } return DC; } function initCharLookupTable(){ var sfcc = String.fromCharCode; for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 clt[i] = sfcc(i); } } this.encode = function(image,quality) // image data object { // var time_start = new Date().getTime(); if(quality) setQuality(quality); // Initialize bit writer byteout = new Array(); bytenew=0; bytepos=7; // Add JPEG headers writeWord(0xFFD8); // SOI writeAPP0(); writeDQT(); writeSOF0(image.width,image.height); writeDHT(); writeSOS(); // Encode 8x8 macroblocks var DCY=0; var DCU=0; var DCV=0; bytenew=0; bytepos=7; this.encode.displayName = "_encode_"; var imageData = image.data; var width = image.width; var height = image.height; var quadWidth = width*4; var tripleWidth = width*3; var x, y = 0; var r, g, b; var start,p, col,row,pos; while(y < height){ x = 0; while(x < quadWidth){ start = quadWidth * y + x; p = start; col = -1; row = 0; for(pos=0; pos < 64; pos++){ row = pos >> 3;// /8 col = ( pos & 7 ) * 4; // %8 p = start + ( row * quadWidth ) + col; if(y+row >= height){ // padding bottom p-= (quadWidth*(y+1+row-height)); } if(x+col >= quadWidth){ // padding right p-= ((x+col) - quadWidth +4) } r = imageData[ p++ ]; g = imageData[ p++ ]; b = imageData[ p++ ]; /* // calculate YUV values dynamically YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); */ // use lookup table (slightly faster) YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; } DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); x+=32; } y+=8; } //////////////////////////////////////////////////////////////// // Do the bit alignment of the EOI marker if ( bytepos >= 0 ) { var fillbits = []; fillbits[1] = bytepos+1; fillbits[0] = (1<<(bytepos+1))-1; writeBits(fillbits); } writeWord(0xFFD9); //EOI var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); byteout = []; // benchmarking // var duration = new Date().getTime() - time_start; // console.log('Encoding time: '+ currentQuality + 'ms'); // return jpegDataUri } function setQuality(quality){ if (quality <= 0) { quality = 1; } if (quality > 100) { quality = 100; } if(currentQuality == quality) return // don't recalc if unchanged var sf = 0; if (quality < 50) { sf = Math.floor(5000 / quality); } else { sf = Math.floor(200 - quality*2); } initQuantTables(sf); currentQuality = quality; // console.log('Quality set to: '+quality +'%'); } function init(){ // var time_start = new Date().getTime(); if(!quality) quality = 50; // Create tables initCharLookupTable() initHuffmanTbl(); initCategoryNumber(); initRGBYUVTable(); setQuality(quality); // var duration = new Date().getTime() - time_start; // console.log('Initialization '+ duration + 'ms'); } init(); }; JPEGEncoder.encode = function( data, quality ) { var encoder = new JPEGEncoder( quality ); return encoder.encode( data ); } return JPEGEncoder; }); /** * @fileOverview Fix android canvas.toDataUrl bug. */ define('runtime/html5/androidpatch',[ 'runtime/html5/util', 'runtime/html5/jpegencoder', 'base' ], function( Util, encoder, Base ) { var origin = Util.canvasToDataUrl, supportJpeg; Util.canvasToDataUrl = function( canvas, type, quality ) { var ctx, w, h, fragement, parts; // 非android手机直接跳过。 if ( !Base.os.android ) { return origin.apply( null, arguments ); } // 检测是否canvas支持jpeg导出,根据数据格式来判断。 // JPEG 前两位分别是:255, 216 if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) { fragement = origin.apply( null, arguments ); parts = fragement.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { fragement = atob( parts[ 1 ] ); } else { fragement = decodeURIComponent( parts[ 1 ] ); } fragement = fragement.substring( 0, 2 ); supportJpeg = fragement.charCodeAt( 0 ) === 255 && fragement.charCodeAt( 1 ) === 216; } // 只有在android环境下才修复 if ( type === 'image/jpeg' && !supportJpeg ) { w = canvas.width; h = canvas.height; ctx = canvas.getContext('2d'); return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality ); } return origin.apply( null, arguments ); }; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: 标记是否被修改过。 modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读取meta信息。 if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,可以删掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需要重新生成。 if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内存。非常重要,否则释放不了image的内存。 this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交换width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // 不允许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // 如果不是ios, 不需要这么复杂! if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/html5/md5',[ 'runtime/html5/runtime' ], function( FlashRuntime ) { /* * Fastest md5 implementation around (JKM md5) * Credits: Joseph Myers * * @see http://www.myersdaily.org/joseph/javascript/md5-text.html * @see http://jsperf.com/md5-shootout/7 */ /* this function is much faster, so if possible we use it. Some IEs are the only ones I know of that need the idiotic second function, generated by an if clause. */ var add32 = function (a, b) { return (a + b) & 0xFFFFFFFF; }, cmn = function (q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }, ff = function (a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }, gg = function (a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }, hh = function (a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }, ii = function (a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }, md5cycle = function (x, k) { var a = x[0], b = x[1], c = x[2], d = x[3]; a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); }, /* there needs to be support for Unicode here, * unless we pretend that we can redefine the MD-5 * algorithm for multi-byte characters (perhaps * by adding every four 16-bit characters and * shortening the sum to 32 bits). Otherwise * I suggest performing MD-5 as if every character * was two bytes--e.g., 0040 0025 = @%--but then * how will an ordinary MD-5 sum be matched? * There is no way to standardize text to something * like UTF-8 before transformation; speed cost is * utterly prohibitive. The JavaScript standard * itself needs to look at this: it should start * providing access to strings as preformed UTF-8 * 8-bit unsigned value arrays. */ md5blk = function (s) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } return md5blks; }, md5blk_array = function (a) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); } return md5blks; }, md51 = function (s) { var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } s = s.substring(i - 64); length = s.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, md51_array = function (a) { var n = a.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk_array(a.subarray(i - 64, i))); } // Not sure if it is a bug, however IE10 will always produce a sub array of length 1 // containing the last element of the parent array if the sub array specified starts // beyond the length of the parent array - weird. // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); length = a.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= a[i] << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], rhex = function (n) { var s = '', j; for (j = 0; j < 4; j += 1) { s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; } return s; }, hex = function (x) { var i; for (i = 0; i < x.length; i += 1) { x[i] = rhex(x[i]); } return x.join(''); }, md5 = function (s) { return hex(md51(s)); }, //////////////////////////////////////////////////////////////////////////// /** * SparkMD5 OOP implementation. * * Use this class to perform an incremental md5, otherwise use the * static methods instead. */ SparkMD5 = function () { // call reset to init the instance this.reset(); }; // In some cases the fast add32 function cannot be used.. if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') { add32 = function (x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF), msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); }; } /** * Appends a string. * A conversion will be applied if an utf8 string is detected. * * @param {String} str The string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.append = function (str) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } // then append as binary this.appendBinary(str); return this; }; /** * Appends a binary string. * * @param {String} contents The binary string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.appendBinary = function (contents) { this._buff += contents; this._length += contents.length; var length = this._buff.length, i; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk(this._buff.substring(i - 64, i))); } this._buff = this._buff.substr(i - 64); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.prototype.end = function (raw) { var buff = this._buff, length = buff.length, i, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; /** * Finish the final calculation based on the tail. * * @param {Array} tail The tail (will be modified) * @param {Number} length The length of the remaining buffer */ SparkMD5.prototype._finish = function (tail, length) { var i = length, tmp, lo, hi; tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(this._state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Do the final computation based on the tail and length // Beware that the final length may not fit in 32 bits so we take care of that tmp = this._length * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(this._state, tail); }; /** * Resets the internal state of the computation. * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.reset = function () { this._buff = ""; this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.prototype.destroy = function () { delete this._state; delete this._buff; delete this._length; }; /** * Performs the md5 hash on a string. * A conversion will be applied if utf8 string is detected. * * @param {String} str The string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hash = function (str, raw) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } var hash = md51(str); return !!raw ? hash : hex(hash); }; /** * Performs the md5 hash on a binary string. * * @param {String} content The binary string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hashBinary = function (content, raw) { var hash = md51(content); return !!raw ? hash : hex(hash); }; /** * SparkMD5 OOP implementation for array buffers. * * Use this class to perform an incremental md5 ONLY for array buffers. */ SparkMD5.ArrayBuffer = function () { // call reset to init the instance this.reset(); }; //////////////////////////////////////////////////////////////////////////// /** * Appends an array buffer. * * @param {ArrayBuffer} arr The array to be appended * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.append = function (arr) { // TODO: we could avoid the concatenation here but the algorithm would be more complex // if you find yourself needing extra performance, please make a PR. var buff = this._concatArrayBuffer(this._buff, arr), length = buff.length, i; this._length += arr.byteLength; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i))); } // Avoids IE10 weirdness (documented above) this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.prototype.end = function (raw) { var buff = this._buff, length = buff.length, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i, ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff[i] << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; /** * Resets the internal state of the computation. * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.reset = function () { this._buff = new Uint8Array(0); this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; /** * Concats two array buffers, returning a new one. * * @param {ArrayBuffer} first The first array buffer * @param {ArrayBuffer} second The second array buffer * * @return {ArrayBuffer} The new array buffer */ SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) { var firstLength = first.length, result = new Uint8Array(firstLength + second.byteLength); result.set(first); result.set(new Uint8Array(second), firstLength); return result; }; /** * Performs the md5 hash on an array buffer. * * @param {ArrayBuffer} arr The array buffer * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.hash = function (arr, raw) { var hash = md51_array(new Uint8Array(arr)); return !!raw ? hash : hex(hash); }; return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( file ) { var blob = file.getSource(), chunkSize = 2 * 1024 * 1024, chunks = Math.ceil( blob.size / chunkSize ), chunk = 0, owner = this.owner, spark = new SparkMD5.ArrayBuffer(), me = this, blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice, loadNext, fr; fr = new FileReader(); loadNext = function() { var start, end; start = chunk * chunkSize; end = Math.min( start + chunkSize, blob.size ); fr.onload = function( e ) { spark.append( e.target.result ); owner.trigger( 'progress', { total: file.size, loaded: end }); }; fr.onloadend = function() { fr.onloadend = fr.onload = null; if ( ++chunk < chunks ) { setTimeout( loadNext, 1 ); } else { setTimeout(function(){ owner.trigger('load'); me.result = spark.end(); loadNext = file = blob = spark = null; owner.trigger('complete'); }, 50 ); } }; fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) ); }; loadNext(); }, getResult: function() { return this.result; } }); }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview 图片压缩 */ define('runtime/flash/image',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Image', { // init: function( options ) { // var owner = this.owner; // this.flashExec( 'Image', 'init', options ); // owner.on( 'load', function() { // debugger; // }); // }, loadFromBlob: function( blob ) { var owner = this.owner; owner.info() && this.flashExec( 'Image', 'info', owner.info() ); owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() ); this.flashExec( 'Image', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview Md5 flash实现 */ define('runtime/flash/md5',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( blob ) { return this.flashExec( 'Md5', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview 完全版本。 */ define('preset/all',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', 'widgets/md5', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/androidpatch', 'runtime/html5/image', 'runtime/html5/transport', 'runtime/html5/md5', // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/transport', 'runtime/flash/blob', 'runtime/flash/md5' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/all' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/plugins/webuploader/webuploader.withoutimage.js ================================================ /*! WebUploader 0.1.5 */ /** * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。 * * AMD API 内部的简单不完全实现,请忽略。只有当WebUploader被合并成一个文件的时候才会引入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简单不完全实现。 // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps不是数组,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部define,暂时不支持不指定id. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // 根据id获取module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径ids装换成对象。 exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom 操作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQuery的Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下提及的功能类,都可以在`WebUploader`这个变量中访问到。 * * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id. * 默认module id为该文件的路径,而此路径将会转化成名字空间存放在WebUploader中。如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。 * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // 反科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,提供一些简单常用的方法。 * @class Base */ return { /** * @property {String} version 当前版本号。 */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简单的浏览器检查结果。 * * * `webkit` webkit版本号,如果浏览器为非webkit内核,此属性为`undefined`。 * * `chrome` chrome浏览器版本号,如果浏览器为chrome,此属性为`undefined`。 * * `ie` ie浏览器版本号,如果浏览器为非ie,此属性为`undefined`。**暂不支持ie10+** * * `firefox` firefox浏览器版本号,如果浏览器为非firefox,此属性为`undefined`。 * * `safari` safari浏览器版本号,如果浏览器为非safari,此属性为`undefined`。 * * `opera` opera浏览器版本号,如果浏览器为非opera,此属性为`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description 操作系统检查结果。 * * * `android` 如果在android浏览器环境下,此值为对应的android版本号,否则为`undefined`。 * * `ios` 如果在ios浏览器环境下,此值为对应的ios版本号,否则为`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] 子类或者对象。如果对象中包含constructor,子类将是用此属性值。 * @param {Function} [protos.constructor] 子类构造器,不指定的话将创建个临时的直接执行父类构造器的方法。 * @param {Object} [statics] 静态属性或方法。 * @return {Class} 返回子类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // 因为没有指定构造器,父类的构造器将会执行。 * var instance = new Manager(); // => Super * * // 继承子父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // 子类的__super__属性指向父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // 复制静态方法 $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让子类的__super__属性指向父类。 child.__super__ = Super.prototype; // 构建原型,添加原型方法或属性。 // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个不做任何事情的方法。可以用来赋值给默认的callback. * @method noop */ noop: noop, /** * 返回一个新的方法,此方法将已指定的`context`来执行。 * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果存在的话,否则引用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当浏览器不在当前窗口时就停了。 // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用来将非数组对象转化成数组对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生成唯一的ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * 格式化文件大小, 输出成带单位的字符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size 文件大小 * @param {Number} [pointLength=2] 精确到的小数点数。 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节,到千字节,一直往上指定。如果单位数组里面只指定了到了K(千字节),同时文件大小大于M, 此方法的输出将还是显示成多少K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处理类,可以独立使用,也可以扩展给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // 根据条件过滤出事件handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 不支持对象,只支持多个event用空格隔开 $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,arguments将会来源于trigger的时候携带的参数。如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`中,某一个方法`return false`了,则后续的其他`callback`都不会被执行到。 * 切会影响到`trigger`方法的返回值,为`false`。 * * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处, * 就是第一个参数为`type`,记录当前是什么事件在触发。此类`callback`的优先级比脚低,会再正常`callback`执行完后触发。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件名,支持多个事件用空格隔开 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完后,自动解除绑定。 * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件名 * @param {Function} callback 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件名 * @param {Function} [callback] 事件处理器 * @param {Object} [context] 事件处理器的上下文。 * @return {self} 返回自身,方便链式 * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触发事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件名 * @param {*} [...] 任意参数 * @return {Boolean} 如果handler中return false了,则返回false, 否则返回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * 中介者,它本身是个单例,但可以通过[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主要目的是负责模块与模块之间的合作,降低耦合度。 * * @class Mediator */ return $.extend({ /** * 可以通过这个接口,使任何对象具备事件功能。 * @method installTo * @param {Object} obj 需要具备事件行为的对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploader上传类 */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * 上传入口类。 * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // 开起分片上传。 * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets中有相应扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // 批量添加纯命令式方法。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * 获取或者设置Uploader配置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // 初始状态图片上传前不会压缩 * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改后图片上传前,尝试将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * 获取文件统计信息。返回一个包含一下信息的对象。 * * `successNum` 上传成功的文件数 * * `progressNum` 上传中的文件数 * * `cancelNum` 被删除的文件数 * * `invalidNum` 无效的文件数 * * `uploadFailNum` 上传失败的文件数 * * `queueNum` 还在队列中的文件数 * * `interruptNum` 被暂停的文件数 * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器 trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // 广播所有uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * 销毁 webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.js将补充此方法的详细文档。 request: Base.noop }); /** * 创建Uploader实例,等同于new Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,可以通过它来扩展业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获取对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接口类。 function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * 添加Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型不能重用,比如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // 允许runtime没有初始化之前,注册一些方法在初始化后执行。 this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // 像filePicker只能独立存在,不能公用。 runtime = runtime || cache.get( null, standalone ); // 需要创建 if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // 来自cache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview 错误信息 */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // 类Backbone的事件监听声明,监听uploader实例上的事件 // widget直接无法监听事件,事件只能通过uploader来传递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // 如果无API响应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 */ // 覆写_init用来初始化widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callback,则用异步方式。 if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很重要不能删除。删除了会死循环。 // 保证执行顺序。让callback总是在下一个 tick 中执行。 return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * 添加组件 * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API 名称与函数实现的映射 * @param {object} proto 组件原型,构造函数通过 constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // 自动生成 map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * 删除插件,只有在注册时指定了名字的才能被删除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件名字 * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // 删除指定的插件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API,且只能通过 mime-type 验证。 * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是否全部允许添加。 dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview 错误信息 */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, 但是知道文件后缀。 if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flash的File和HTML5的File而存在。 * 以至于要调用Flash里面的File,也可以像调用HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支持其他类型文件的转换。 // 如果有 mimetype, 但是文件名里面没有找出后缀规律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview 错误信息 */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // 记录来源。 file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description 指定选择文件的按钮容器,不指定则不创建按钮。 * * * `id` {Seletor} 指定选择文件的按钮容器,不指定则不创建按钮。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。 * * `multiple` {Boolean} 是否开起同时选择多个文件能力。 */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 * * * `title` {String} 文字描述 * * `extensions` {String} 允许的文件后缀,不带点,多个用逗号分割。 * * `mimeTypes` {String} 多个用逗号分割。 * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * 添加文件选择按钮,如果一个按钮不够,需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview 文件属性封装 */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor 构造函数 * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。 */ function WUFile( source ) { /** * 文件名,包括扩展名(后缀) * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(字节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最后修改日期 * @property lastModifiedDate * @type {int} * @default 当前时间戳 */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,每个对象具有唯一ID,与文件名无关 * @property id * @type {string} */ this.id = gid(); /** * 文件扩展名,通过文件名获取,例如test.png的扩展名为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * 状态文字说明。在不同的status语境下有不同的用途。 * @property statusText * @type {string} */ this.statusText = ''; // 存储文件状态,防止通过属性直接修改 statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状态,状态变化时会触发`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状态说明,常在error时使用,用http, abort,server等来标记是由于什么原因导致文件错误。 */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状态变化 * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * 获取文件状态 * @return {File.Status} * @example 文件状态具体包括以下几种类型: { // 初始化 INITED: 0, // 已入队列 QUEUED: 1, // 正在上传 PROGRESS: 2, // 上传出错 ERROR: 3, // 上传成功 COMPLETE: 4, // 上传取消 CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * 获取文件原始信息。 * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状态值,具体包括以下几种类型: * * `inited` 初始状态 * * `queued` 已经进入队列, 等待上传 * * `progress` 上传中 * * `complete` 上传完成。 * * `error` 上传出错,可重试 * * `interrupt` 上传中断,可续传。 * * `invalid` 文件不合格,不能重试上传。会自动从队列中移除。 * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // 初始状态 QUEUED: 'queued', // 已经进入队列, 等待上传 PROGRESS: 'progress', // 上传中 ERROR: 'error', // 上传出错,可重试 COMPLETE: 'complete', // 上传完成。 CANCELLED: 'cancelled', // 上传取消。 INTERRUPT: 'interrupt', // 上传中断,可续传。 INVALID: 'invalid' // 文件不合格,不能重试上传。 }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用来存储各个状态中的文件。 * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列中的文件数。 * * `numOfSuccess` 上传成功的文件数 * * `numOfCancel` 被取消的文件数 * * `numOfProgress` 正在上传中的文件数 * * `numOfUploadFailed` 上传错误的文件数。 * * `numOfInvalid` 无效的文件数。 * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0, }; // 上传队列,仅包括等待上传的文件 this._queue = []; // 存储所有文件 this._map = {}; } $.extend( Queue.prototype, { /** * 将新文件加入对队列尾部 * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * 将新文件加入对队列头部 * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * 获取文件对象 * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列中取出一个指定状态的文件。 * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状态值](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排序,能够控制文件上传顺序。 * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn 排序方法 */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状态值](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列中删除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // accept中的中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当前不是html5运行时,那就算了。 // 不执行后续操作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 运行时的 placeholder // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支持外部直接添加一个原生File对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // 判断文件是否可以被加入队列 acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果名字中有后缀,才做后缀白名单处理。 rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description 当文件被加入队列之前触发,此事件的handler返回值为`false`,则此文件不会被添加进入队列。 * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description 当文件被加入队列以后触发。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // 不过类型判断允许不允许,先派送 `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型不匹配,则派送错误事件,并返回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description 当一批文件添加进队列以后触发。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description 添加文件到队列 * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列后触发。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为 `true` 则会从 queue 中移除。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状态的文件集合,不传参数将返回所有状态的文件。 * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description 重试上传,重试指定文件,或者从出错的文件开始重新上传。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排序队列中的文件,在上传之前调整可以控制上传顺序。 * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被重置的时候触发。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description 重置uploader。目前只重置了队列。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview 添加获取Runtime相关信息的方法。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是否允许携带cookie, 只有html5 runtime才有效 withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // 添加Blob, 只能添加一次,最后一次有效。 appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的同一个runtime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // 添加其他字段 append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview 负责文件上传相关。 */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // 添加默认配置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是否允许在文件传输时提前把下一个文件准备好。 * 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 * 如果能提前在当前文件传输期处理,可以节省总体耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是否要分片处理大文件上传。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果要分片,分多大一片? 默认大小为5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果某个分片由于网络问题出错,允许自动重传多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description 上传并发数。允许同时最大上传进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description 文件上传请求的参数表,每次发送都会发送此对象中的参数。 */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description 设置文件上传域的name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description 文件上传方式,`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是否已二进制的流的方式发送文件,这样整个上传内容`php://input`都为文件内容, * 其他参数在$_GET数组中。 */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // 记录当前正在传的数据,跟threads相关 this.pool = []; // 缓存分好片的文件。 this.stack = []; // 缓存即将上传的文件。 this.pending = []; // 跟踪还有多少分片在上传中但是没有完成上传。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他块取消了。 file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description 当开始上传流程时触发。 * @for Uploader */ /** * 开始上传。此方法可以从初始状态调用开始上传流程,也可以从暂停状态调用,继续上传流程。 * * 可以指定开始某一个文件。 * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始某个文件,则只开始指定文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之前暂停过。 if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; // 如果有暂停的,则续传 $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { file.setStatus( Status.PROGRESS ); me._trigged = false; v.transport && v.transport.send(); } }); file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description 当开始上传流程暂停时触发。 * @for Uploader */ /** * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。 * * 如果第一个参数是文件,则只暂停指定文件。 * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // 如果只是暂停某个文件。 if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // 只 abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description 标记文件状态为已取消, 同时将中断文件传输。 * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判断`Uplaode`r是否正在上传中。 * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * 掉过一个文件上传,直接标记指定文件为已上传状态。 * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果正在上传。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description 当所有文件上传结束时触发。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结束,则等待完成后再执行。 if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有位置,且还有文件要处理的话。 if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有可能是reject过来的,所以要检测val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有要上传的了,且没有正在传输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已经处理完了的,或者,状态为非 progress(上传中)、 // interupt(暂停中) 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // 如果当前文件还有没有需要传输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // 是否提前准备下一个文件 if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // 否则,如果正在运行,则准备下一个文件,并等待完成后返回下个分片。 } else if ( me.runing ) { // 如果缓存中有,则直接在缓存中取,没有则去queue中取。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件可能还在prepare中,也有可能已经完全准备好了。 if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description 某个文件开始上传前触发,一个文件只会触发一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有可能文件被skip掉了。文件被skip掉后,状态坑定不是Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pending中,则替换成文件本身。 promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩子就有错误发生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出位置了,可以让其他分片开始上传 _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // 开始上传,可以被掉过。如果promise被reject了,则表示跳过此分片。 _startSend: function( block ) { var me = this, file = block.file, promise; // 有可能在 before-send-file 的 promise 期间改变了文件状态。 // 如:暂停,取消 // 我们不能中断 promise, 但是可以在 promise 完后,不做上传操作。 if ( file.getStatus() !== Status.PROGRESS ) { // 如果是中断,则还需要放回去。 if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // 不会丢失content-type信息。 block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, 每个分片发送之前可能要做些异步的事情。 promise = me.request( 'before-send', block, function() { // 有可能文件已经上传出错了,所以不需要再传输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过此分片。 promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数。 * @param {Object} headers 可以扩展此对象来控制上传头部。 * @description 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret 服务端的返回数据,json格式,如果服务端不是json格式,从ret._raw中取数据,自行解析。 * @description 当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage 上传进度 * @description 上传过程中触发,携带上传进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description 当文件上传出错时触发。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response 服务端返回的数据 * @description 当文件上传成功时触发。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description 不管成功或者失败,文件上传完成时触发。 * @for Uploader */ // 做上传操作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广播上传进度。以文件为单位。 tr.on( 'progress', function( percentage ) { var totalPercent = 0, uploaded = 0; // 可能没有abort掉,progress还是执行进来了。 // if ( !file.blocks ) { // return; // } totalPercent = block.percentage = percentage; if ( block.chunks > 1 ) { // 计算文件的整体速度。 $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; } owner.trigger( 'uploadProgress', file, totalPercent || 0 ); }); // 用来询问,是否返回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // 服务端响应了,不代表成功了,询问是否响应正确。 if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // 尝试重试,然后广播文件上传出错。 tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动重试 if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // 上传成功 tr.on( 'load', function() { var reason; // 如果非预期,转向上传出错。 if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // 全部上传完成。 if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // 配置默认的上传字段。 data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在发送之间可以添加字段什么的。。。 // 如果默认的字段不够使用,可以通过监听此事件来扩展 owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始发送。 tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完成上传。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // 如果外部已经标记为invalid什么的,不再改状态。 if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); } }); }); /** * @fileOverview 各种验证,包括文件总大小是否超出、单文件是否超出和文件是否重复。 */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validate不通过时,会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误,目前有以下错误会在特定的情况下派送错来。 * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。 * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。 * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。 * @for Uploader */ // 暴露给外面的api api = { // 添加验证器 addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验证器 removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploader初始化的时候启动Validators的初始化 Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总数量, 超出则不允许加入队列。 */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。 */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // 已经重复了 if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Runtime管理器,负责Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // 不需要连接其他程序,直接执行callback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5运行时。 // 只有在支持的前提下注册。 if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注意只有 chrome 支持。 items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // 只处理框内的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // 只处理框内的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页面内拖拽,还不能处理,不阻止事件。 // 此处 ie11 下会报参数错误, try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // 如果传入 callback 则去查看文件夹,否则只管当前文件夹。 _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了保证顺序。 i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetp的mimeTypes中生成匹配正则。 if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // 不阻止非文件粘贴(文字粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox不支持单独指定后缀 if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * @fileOverview Transport * @todo 支持chunked传输,优势: * 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分, * 而不需要重头再传一次。另外断点续传也需要用chunked方式。 */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // 强制设置成 content-type 为文件流。 xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接发送blob会导致服务端接收到的是空文件。 // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读取出来再通过arraybuffer的方式发送。 if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接受器。 window[ jsreciver ] = function() { var args = arguments; // 为了能捕获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo 删除池子中的所有实例 return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '' + '' + '' + '' + ''; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处理可能存在 bug, 没辙只能靠 js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview 没有图像处理的版本。 */ define('preset/withoutimage',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/transport', // flash 'runtime/flash/filepicker', 'runtime/flash/transport' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/withoutimage' ], function( preset ) { return preset; }); return require('webuploader'); }); ================================================ FILE: material-manage/src/main/webapp/static/js/read-me.txt ================================================ I have created a seperate folder(flot-charts) for Flot Chart items. Each and every Flot chart I have used in this template have it's own js file inside flot-charts folder in order to get a clear picture of codes. ================================================ FILE: material-manage/src/main/webapp/static/lab/README.md ================================================ 实验性质功能静态资源目录 如果不用可以将该目录和WEB-INF.view/lab目录一并删除 ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/base/base_leaflet.draw.ext.js ================================================ L.Draw.Rectangle.Query = L.Draw.Rectangle .extend({ statics : { TYPE : 'rectangleQuery' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Rectangle.Query.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start; // 自定义的 this._RectangleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._RectangleLayerGroup); L.Draw.SimpleShape.prototype.initialize .call(this, map, options); }, _onMouseUp : function(e) { var obj = new Array(); obj[0] = this._shape.getBounds()._northEast; obj[2] = this._shape.getBounds()._southWest; obj[1] = L.latLng(this._shape.getBounds()._southWest.lat, this._shape.getBounds()._northEast.lng); obj[3] = L.latLng(this._shape.getBounds()._northEast.lat, this._shape.getBounds()._southWest.lng); var latLngs = obj[0].lat + ',' + obj[0].lng + ';' + obj[1].lat + ',' + obj[1].lng + ';' + obj[2].lat + ',' + obj[2].lng + ';' + obj[3].lat + ',' + obj[3].lng; mapAreaSearchInfo(latLngs, "rect", this._RectangleLayerGroup); if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Circle.Query = L.Draw.Circle .extend({ statics : { TYPE : 'circleQuery' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true }, showRadius : true, metric : true // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Circle.Query.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; // 自定义的 this._CircleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._CircleLayerGroup); L.Draw.SimpleShape.prototype.initialize .call(this, map, options); }, _onMouseUp : function(e) { var latLngs = this._startLatLng.lat.toString() + ',' + this._startLatLng.lng.toString(); var radius = this._shape.getRadius().toFixed(1); mapAreaSearchInfo(latLngs + ";" + radius, "circle", this._CircleLayerGroup); if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Polygon.Query = L.Draw.Polyline.extend({ statics : { TYPE : 'polygonQuery' }, Poly : L.Polygon, options : { showArea : true, shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; L.Draw.Polyline.prototype.initialize.call(this, map, options); // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Polygon.Query.TYPE; }, _finishShape : function() { var latLngsArray = this._poly.getLatLngs(); var latLngs = ''; for ( var i = 0; i < latLngsArray.length; i++) { latLngs += latLngsArray[i].lat.toString() + ',' + latLngsArray[i].lng.toString() + ';'; } latLngs = latLngs.toString().substring(0, (latLngs.length - 1)) + ''; mapAreaSearchInfo(latLngs, "polygon", this._drawnItems); var intersects = this._poly.newLatLngIntersects( this._poly.getLatLngs()[0], true); if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { this._showErrorTooltip(); return; } this._fireCreatedEvent(); this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Reset = L.Draw.SimpleShape.extend({ statics : { TYPE : 'reset' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Reset.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.CleanAll = L.Draw.SimpleShape.extend({ statics : { TYPE : 'cleanAll' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.CleanAll.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } // 清空非工具条生成的图层 if (L.Draw.customData) { this._map.removeLayer(L.Draw.customData); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.ZoomIn = L.Draw.SimpleShape.extend({ statics : { TYPE : 'zoomIn' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; this.type = L.Draw.ZoomIn.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { this.zoom = this._map.getZoom(); if (this.zoom < 16) { this._map.zoomIn(); // this._map.setZoom(this.zoom+1); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.ZoomOut = L.Draw.SimpleShape.extend({ statics : { TYPE : 'zoomOut' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; this.type = L.Draw.ZoomOut.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { this.zoom = this._map.getZoom(); if (this.zoom > 4) { this._map.zoomOut(); // this._map.setZoom(this.zoom-1); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); // 资源流向 function orderPolyline() { var arrow = L.polyline( [ [ 39.33429742980725, 106.72119140625 ], [ 39.8928799002948, 116.43310546875 ], [ 41.78769700539063, 123.46435546875 ] ], {}).addTo(map); createLine(arrow); var arrow1 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 45.60635207711834, 122.82714843749999 ] ], {}).addTo(map); createLine(arrow1); var arrow2 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 45.775186183521036, 126.57348632812499 ] ], {}) .addTo(map); createLine(arrow2); var arrow3 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 43.24520272203356, 128.34228515625 ] ], {}).addTo(map); createLine(arrow3); var arrow4 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 44.35527821160296, 124.67285156250001 ] ], {}).addTo(map); createLine(arrow4); // 122.82714843749999 45.60635207711834 var c1 = L.circle([ 45.60635207711834, 122.82714843749999 ], 20000).addTo( map); var c2 = L.circle([ 45.775186183521036, 126.57348632812499 ], 20000).addTo( map); var c3 = L.circle([ 43.24520272203356, 128.34228515625 ], 20000).addTo(map); var c4 = L.circle([ 44.35527821160296, 124.67285156250001 ], 20000).addTo( map); // 航线图 var pathPattern = L.polylineDecorator( [ [ 26.51, 122.61 ], [ 32.36, 123.40 ], [ 35.21, 120.63 ], [ 37.51, 123.88 ], [ 38.89, 119.18 ] ], { patterns : [ { offset : 0, repeat : 10, symbol : L.Symbol.dash({ pixelSize : 5, pathOptions : { color : '#000', weight : 1, opacity : 0.2 } }) }, { offset : '16%', repeat : '33%', symbol : L.Symbol.marker({ rotate : true, markerOptions : { icon : L.icon({ iconUrl : '../icon_plane.png', iconAnchor : [ 16, 16 ] }) } }) } ] }).addTo(map); } function createLine(arrow) { var arrowHead = L.polylineDecorator(arrow).addTo(map); var arrowOffset = 0; var anim = window.setInterval(function() { arrowHead.setPatterns([ { offset : arrowOffset + '%', repeat : 0, symbol : L.Symbol.arrowHead({ pixelSize : 13, polygon : false, pathOptions : { stroke : true } }) } ]); if (++arrowOffset > 100) arrowOffset = 0; }, 200); } function addHistogram(provinceMap) { if (removeHistogram) { this.histogramLayer = new L.FeatureGroup(); var myIcon = L .divIcon({ iconSize : L.point(1100, 200), iconAnchor : L.point(1150, 205), html : ' ' }); var beiJingMarker = L.marker([ 39.92, 116.41 ], { icon : myIcon }).addTo(map); var myIcon1 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '山西' }); var shanxiMarker = L.marker([ 37.87, 112.54 ], { icon : myIcon1 }).addTo(map); var myIcon2 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '内蒙古' }); var neimenggugMarker = L.marker([ 40.86, 111.75 ], { icon : myIcon2 }).addTo(map); var myIcon3 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '河北' }); var hebeiMarker = L.marker([ 38.07, 114.50 ], { icon : myIcon3 }).addTo(map); var myIcon4 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '山东' }); var shandongMarker = L.marker([ 36.72, 117.09 ], { icon : myIcon4 }).addTo(map); histogramLayer.addLayer(beiJingMarker, shanxiMarker, neimenggugMarker, hebeiMarker, shandongMarker); map.addLayer(histogramLayer); } } function removeHistogram() { if (histogramLayer) { map.removeLayer(this.histogramLayer); } } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/base/base_map.js ================================================ var map; var zTree; var markerArray = new Array(); var routeLayer = null; function initMap(mapServerUrl) { var funcLayer = new L.TileLayer.Functional(function (view) { var url = "http://172.16.247.100/gis"+'/{z}/{y}/{x}.png' .replace('{z}', "L" +(parseInt(view.zoom)<10?("0"+view.zoom):view.zoom)) .replace('{x}', (function(){ var x = parseInt(view.tile.column, 10).toString(16) if(x.length < 8){ var k = x.length; for(var i = 0; i < 8 - k;i++){ x = "0" + x; } } x = "C" + x; //console.log(x + " length :" + x.length); return x.toUpperCase(); })()) .replace('{y}', (function(){ var y = parseInt(view.tile.row, 10).toString(16) if(y.length < 8){ var k = y.length; for(var i = 0; i < 8 - k;i++){ y = "0" + y; } } y = "R" + y; return y.toUpperCase(); })()) .replace('{s}', view.subdomain); return url; }, { subdomains: '1234',opacity:1 }); map = L.map('map', { center: [39.918, 116.38], zoom: 5, zoomControl:false, layers: [funcLayer] , maxZoom : 16, minZoom:4, maxBounds:[[85.03,-179.82],[-85,179.82]] }); var baseLayers = { "Google量地图": funcLayer }; routeLayer = L.layerGroup().addTo(map); L.control.layers(baseLayers).addTo(map); var drawnItems = new L.FeatureGroup(); map.addLayer(drawnItems); var drawControl = new L.Control.Draw({ draw: { position: 'topleft', polygon: { title: '多边形!', allowIntersection: false, drawError: { color: '#b00b00', timeout: 1000 }, shapeOptions: { color: 'blue' }, showArea: true }, polyline: { metric: false }, circle: { shapeOptions: { color: '#662d91' } } } }); map.addControl(drawControl); map.on('draw:created', function (e) { var type = e.layerType, layer = e.layer; drawnItems.addLayer(layer); }); map.on("zoomstart", function(e){ }); map.on("zoomend", function(e){ var zoom = e.target._zoom; var items = null; if(parseInt(zoom) <=5 ){//全国 } else if(parseInt(zoom) >=5&&parseInt(zoom) <=7){//省级 }else{//市级 } }); } //增加地图点击事件 function addMapClickEvent(){ map.on("click", function(e){ clickMap(e.latlng.lat,e.latlng.lng); }); } //移除地图点击事件 function remMapClickEvent(){ map.off("click"); } function dingwei(lat,lng,zoom){ map.panTo(L.latLng(lat, lng)); map.setZoom(zoom); } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/base/base_tilesUtils.js ================================================ //叠加点数量多 用 叠加点图层方式 function drawPointTile(table,where){ var url = 'wms/drawTitles?x={x}&y={y}&z={z}&table='+table+"&where="+where; var layName = 'pointTitle'; addTiles(url,layName); } //根据tileUrl 返回瓦片图 var keyList = new Array();//存储数节点的key值 var layerList = new Array();//存储对应key的layer //titleUrl 请求地址,key 图层名称 function addTiles(tileUrl, key){ if(key=='404'){ addHistogram(); return; } var tilesLayer = L.tileLayer(tileUrl, { maxZoom : 18, zIndex:20 }); var flag = false; for(var i=0; ienilu.cn' }).addTo(map); this.map = map; }, /** * 添加坐标点 * @param data */ addMark: function (data) { var marker = L.marker([data.lat, data.lng]).addTo(this.map); //设置自定义属性 marker.jsonObj = data; var me = this; marker.on("click", function () { me.clickMark(this); }); marker.on("mouseover", function () { me.mouseOverEvent(this); }); if (data.name) { marker.bindPopup(data.name); } }, /** * 添加多个坐标点 * @param positionList */ addMarks: function (positionList) { for (var i = 0; i < positionList.length; i++) { this.addMark(positionList[i]); } }, /** * 定位 * @param lat * @param lng */ pointTo:function(lat,lng){ this.addMark({lat:31.235958,lng:121.480545,name:'上海'}); this.map.panTo(L.latLng(lat, lng)); }, /** * 画圆 * @param data */ drawCircle:function(data){ var me = this; var barLayer = new BarLayer({ data : data, map : me.map }); barLayer.addLayer(); this.innerData.barLayer = barLayer; }, /** * 画弧线 * @param data */ drawArc:function(data){ var me = this; var arcLayer = new ArcLayer({ data : data, map : me.map }); arcLayer.addLayer(); this.innerData.arcLayer = arcLayer; } } $(document).ready(function () { flashMap.init("map"); $('#btnInit').hide(); $('#btnAddMark').click(function () { flashMap.addMark({lat: 40.13254611, lng: 116.5056088, name: "北京"}); }) $("#btnAddMarks").click(function () { flashMap.addMarks([ {lat: 37.94, lng: 112.64, name: '太原'}, {lat: 40.82149932, lng: 111.6842769, name: '呼和浩特市'}]); }) $('#btnPointTo').click(function(){ flashMap.pointTo(31.235958,121.480545,8); }) var drawCircle = true $('#btnDrawCircle').click(function(){ if(drawCircle) { flashMap.drawCircle(cirlData); $(this).text('删除圆圈'); drawCircle=false; }else{ flashMap.innerData.barLayer.removeLayer(); flashMap.innerData.barLayer = null; $(this).text('画圆圈').show(); drawCircle = true; } }); var drawArc = true; $('#btnDrawArc').click(function(){ var arcData={ style:{color:'red',thickness:4,speed:10}, isOut:'1', centerLatLng:{lat:31.1,lng:116.1}, latLng:[{lat:41.802542590364354,lng:123.431396484375}, {lat:34.252676117101515,lng:108.896484375}, {lat:29.611670115197377,lng:106.5234375}, {lat:42.81152174509788,lng:113.466796875}, {lat:40.27952566881291,lng:86.0009765625}] }; if(drawArc){ flashMap.drawArc(arcData); $(this).text('删除弧线'); drawArc=false; }else{ flashMap.innerData.arcLayer.removeLayer(); flashMap.innerData.arcLayer = null; $(this).text('画弧线'); drawArc=true; } }) }) ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/base/base_leaflet.draw.ext.js ================================================ L.Draw.Rectangle.Query = L.Draw.Rectangle .extend({ statics : { TYPE : 'rectangleQuery' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Rectangle.Query.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start; // 自定义的 this._RectangleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._RectangleLayerGroup); L.Draw.SimpleShape.prototype.initialize .call(this, map, options); }, _onMouseUp : function(e) { var obj = new Array(); obj[0] = this._shape.getBounds()._northEast; obj[2] = this._shape.getBounds()._southWest; obj[1] = L.latLng(this._shape.getBounds()._southWest.lat, this._shape.getBounds()._northEast.lng); obj[3] = L.latLng(this._shape.getBounds()._northEast.lat, this._shape.getBounds()._southWest.lng); var latLngs = obj[0].lat + ',' + obj[0].lng + ';' + obj[1].lat + ',' + obj[1].lng + ';' + obj[2].lat + ',' + obj[2].lng + ';' + obj[3].lat + ',' + obj[3].lng; mapAreaSearchInfo(latLngs, "rect", this._RectangleLayerGroup); if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Circle.Query = L.Draw.Circle .extend({ statics : { TYPE : 'circleQuery' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true }, showRadius : true, metric : true // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Circle.Query.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; // 自定义的 this._CircleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._CircleLayerGroup); L.Draw.SimpleShape.prototype.initialize .call(this, map, options); }, _onMouseUp : function(e) { var latLngs = this._startLatLng.lat.toString() + ',' + this._startLatLng.lng.toString(); var radius = this._shape.getRadius().toFixed(1); mapAreaSearchInfo(latLngs + ";" + radius, "circle", this._CircleLayerGroup); if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Polygon.Query = L.Draw.Polyline.extend({ statics : { TYPE : 'polygonQuery' }, Poly : L.Polygon, options : { showArea : true, shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; L.Draw.Polyline.prototype.initialize.call(this, map, options); // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Polygon.Query.TYPE; }, _finishShape : function() { var latLngsArray = this._poly.getLatLngs(); var latLngs = ''; for ( var i = 0; i < latLngsArray.length; i++) { latLngs += latLngsArray[i].lat.toString() + ',' + latLngsArray[i].lng.toString() + ';'; } latLngs = latLngs.toString().substring(0, (latLngs.length - 1)) + ''; mapAreaSearchInfo(latLngs, "polygon", this._drawnItems); var intersects = this._poly.newLatLngIntersects( this._poly.getLatLngs()[0], true); if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { this._showErrorTooltip(); return; } this._fireCreatedEvent(); this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Reset = L.Draw.SimpleShape.extend({ statics : { TYPE : 'reset' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Reset.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.CleanAll = L.Draw.SimpleShape.extend({ statics : { TYPE : 'cleanAll' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.CleanAll.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } // 清空非工具条生成的图层 if (L.Draw.customData) { this._map.removeLayer(L.Draw.customData); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.ZoomIn = L.Draw.SimpleShape.extend({ statics : { TYPE : 'zoomIn' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; this.type = L.Draw.ZoomIn.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { this.zoom = this._map.getZoom(); if (this.zoom < 16) { this._map.zoomIn(); // this._map.setZoom(this.zoom+1); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); L.Draw.ZoomOut = L.Draw.SimpleShape.extend({ statics : { TYPE : 'zoomOut' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, text : 'aaa', clickable : true } // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; this.type = L.Draw.ZoomOut.TYPE; L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, addHooks : function() { this.zoom = this._map.getZoom(); if (this.zoom > 4) { this._map.zoomOut(); // this._map.setZoom(this.zoom-1); } this.disable(); if (this.options.repeatMode) { this.enable(); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } } }); // 资源流向 function orderPolyline() { var arrow = L.polyline( [ [ 39.33429742980725, 106.72119140625 ], [ 39.8928799002948, 116.43310546875 ], [ 41.78769700539063, 123.46435546875 ] ], {}).addTo(map); createLine(arrow); var arrow1 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 45.60635207711834, 122.82714843749999 ] ], {}).addTo(map); createLine(arrow1); var arrow2 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 45.775186183521036, 126.57348632812499 ] ], {}) .addTo(map); createLine(arrow2); var arrow3 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 43.24520272203356, 128.34228515625 ] ], {}).addTo(map); createLine(arrow3); var arrow4 = L.polyline( [ [ 41.78769700539063, 123.46435546875 ], [ 44.35527821160296, 124.67285156250001 ] ], {}).addTo(map); createLine(arrow4); // 122.82714843749999 45.60635207711834 var c1 = L.circle([ 45.60635207711834, 122.82714843749999 ], 20000).addTo( map); var c2 = L.circle([ 45.775186183521036, 126.57348632812499 ], 20000).addTo( map); var c3 = L.circle([ 43.24520272203356, 128.34228515625 ], 20000).addTo(map); var c4 = L.circle([ 44.35527821160296, 124.67285156250001 ], 20000).addTo( map); // 航线图 var pathPattern = L.polylineDecorator( [ [ 26.51, 122.61 ], [ 32.36, 123.40 ], [ 35.21, 120.63 ], [ 37.51, 123.88 ], [ 38.89, 119.18 ] ], { patterns : [ { offset : 0, repeat : 10, symbol : L.Symbol.dash({ pixelSize : 5, pathOptions : { color : '#000', weight : 1, opacity : 0.2 } }) }, { offset : '16%', repeat : '33%', symbol : L.Symbol.marker({ rotate : true, markerOptions : { icon : L.icon({ iconUrl : '../icon_plane.png', iconAnchor : [ 16, 16 ] }) } }) } ] }).addTo(map); } function createLine(arrow) { var arrowHead = L.polylineDecorator(arrow).addTo(map); var arrowOffset = 0; var anim = window.setInterval(function() { arrowHead.setPatterns([ { offset : arrowOffset + '%', repeat : 0, symbol : L.Symbol.arrowHead({ pixelSize : 13, polygon : false, pathOptions : { stroke : true } }) } ]); if (++arrowOffset > 100) arrowOffset = 0; }, 200); } function addHistogram(provinceMap) { if (removeHistogram) { this.histogramLayer = new L.FeatureGroup(); var myIcon = L .divIcon({ iconSize : L.point(1100, 200), iconAnchor : L.point(1150, 205), html : ' ' }); var beiJingMarker = L.marker([ 39.92, 116.41 ], { icon : myIcon }).addTo(map); var myIcon1 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '山西' }); var shanxiMarker = L.marker([ 37.87, 112.54 ], { icon : myIcon1 }).addTo(map); var myIcon2 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '内蒙古' }); var neimenggugMarker = L.marker([ 40.86, 111.75 ], { icon : myIcon2 }).addTo(map); var myIcon3 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '河北' }); var hebeiMarker = L.marker([ 38.07, 114.50 ], { icon : myIcon3 }).addTo(map); var myIcon4 = L.divIcon({ iconSize : L.point(100, 20), iconAnchor : L.point(50, 25), html : '山东' }); var shandongMarker = L.marker([ 36.72, 117.09 ], { icon : myIcon4 }).addTo(map); histogramLayer.addLayer(beiJingMarker, shanxiMarker, neimenggugMarker, hebeiMarker, shandongMarker); map.addLayer(histogramLayer); } } function removeHistogram() { if (histogramLayer) { map.removeLayer(this.histogramLayer); } } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/base/base_map.js ================================================ var map; var zTree; var markerArray = new Array(); var routeLayer = null; function initMap(mapServerUrl) { var funcLayer = new L.TileLayer.Functional(function (view) { var url = "http://gisdata.enilu.cn"+'/{z}/{y}/{x}.png' .replace('{z}', "L" +(parseInt(view.zoom)<10?("0"+view.zoom):view.zoom)) .replace('{x}', (function(){ var x = parseInt(view.tile.column, 10).toString(16) if(x.length < 8){ var k = x.length; for(var i = 0; i < 8 - k;i++){ x = "0" + x; } } x = "C" + x; //console.log(x + " length :" + x.length); return x.toUpperCase(); })()) .replace('{y}', (function(){ var y = parseInt(view.tile.row, 10).toString(16) if(y.length < 8){ var k = y.length; for(var i = 0; i < 8 - k;i++){ y = "0" + y; } } y = "R" + y; return y.toUpperCase(); })()) .replace('{s}', view.subdomain); return url; }, { subdomains: '1234',opacity:1 }); map = L.map('map', { center: [39.918, 116.38], zoom: 5, zoomControl:false, layers: [funcLayer] , maxZoom : 16, minZoom:4, maxBounds:[[85.03,-179.82],[-85,179.82]] }); var baseLayers = { "Google量地图": funcLayer }; routeLayer = L.layerGroup().addTo(map); L.control.layers(baseLayers).addTo(map); var drawnItems = new L.FeatureGroup(); map.addLayer(drawnItems); var drawControl = new L.Control.Draw({ draw: { position: 'topleft', polygon: { title: '多边形!', allowIntersection: false, drawError: { color: '#b00b00', timeout: 1000 }, shapeOptions: { color: 'blue' }, showArea: true }, polyline: { metric: false }, circle: { shapeOptions: { color: '#662d91' } } } }); map.addControl(drawControl); map.on('draw:created', function (e) { var type = e.layerType, layer = e.layer; drawnItems.addLayer(layer); }); map.on("zoomstart", function(e){ }); map.on("zoomend", function(e){ var zoom = e.target._zoom; var items = null; if(parseInt(zoom) <=5 ){//全国 } else if(parseInt(zoom) >=5&&parseInt(zoom) <=7){//省级 }else{//市级 } }); } //增加地图点击事件 function addMapClickEvent(){ map.on("click", function(e){ clickMap(e.latlng.lat,e.latlng.lng); }); } //移除地图点击事件 function remMapClickEvent(){ map.off("click"); } function dingwei(lat,lng,zoom){ map.panTo(L.latLng(lat, lng)); map.setZoom(zoom); } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/base/base_tilesUtils.js ================================================ //叠加点数量多 用 叠加点图层方式 function drawPointTile(table,where){ var url = 'wms/drawTitles?x={x}&y={y}&z={z}&table='+table+"&where="+where; var layName = 'pointTitle'; addTiles(url,layName); } //根据tileUrl 返回瓦片图 var keyList = new Array();//存储数节点的key值 var layerList = new Array();//存储对应key的layer //titleUrl 请求地址,key 图层名称 function addTiles(tileUrl, key){ if(key=='404'){ addHistogram(); return; } var tilesLayer = L.tileLayer(tileUrl, { maxZoom : 18, zIndex:20 }); var flag = false; for(var i=0; i 0){ POINT_COUNT = pointCount; } var curvePoints = getCurvePoints(points); var polyline = new L.Polyline(curvePoints, opts); return polyline; } /** * 根据弧线的坐标节点数组 */ getCurvePoints = function(points) { var curvePoints = []; for (var i = 0; i < points.length - 1; i++) { var p = getCurveByTwoPoints(points[i], points[i + 1]); if (p && p.length > 0) { curvePoints = curvePoints.concat(p); } } return curvePoints; } /** * 根据两点获取曲线坐标点数组 * @param Point 起点 * @param Point 终点 */ getCurveByTwoPoints = function(obj1, obj2) { if (!obj1 || !obj2 || !(obj1 instanceof L.LatLng) || !(obj2 instanceof L.LatLng)) { return null; } var B1 = function(x) { return 1 - 2 * x + x * x; }; var B2 = function(x) { return 2 * x - 2 * x * x; }; var B3 = function(x) { return x * x; }; curveCoordinates = []; var isFuture=false; var t, h, h2, lat3, lng3, j, t2; var LnArray = []; var i = 0; var inc = 0; if (typeof(obj2) == "undefined") { if (typeof(curveCoordinates) != "undefined") { curveCoordinates = []; } return; } var lat1 = parseFloat(obj1.lat); var lat2 = parseFloat(obj2.lat); var lng1 = parseFloat(obj1.lng); var lng2 = parseFloat(obj2.lng); // 计算曲线角度的方法 if (lng2 > lng1) { if (parseFloat(lng2-lng1) > 180) { if (lng1 < 0) { lng1 = parseFloat(180 + 180 + lng1); } } } if (lng1 > lng2) { if (parseFloat(lng1-lng2) > 180) { if (lng2 < 0) { lng2 = parseFloat(180 + 180 + lng2); } } } j = 0; t2 = 0; if (lat2 == lat1) { t = 0; h = lng1 - lng2; } else if (lng2 == lng1) { t = Math.PI / 2; h = lat1 - lat2; } else { t = Math.atan((lat2 - lat1) / (lng2 - lng1)); h = (lat2 - lat1) / Math.sin(t); } if (t2 == 0) { t2 = (t + (Math.PI / 5)); } h2 = h / 2; lng3 = h2 * Math.cos(t2) + lng1; lat3 = h2 * Math.sin(t2) + lat1; for (i = 0; i < POINT_COUNT + 1; i++) { curveCoordinates.push(L.latLng( (lat1 * B1(inc) + lat3 * B2(inc) + lat2 * B3(inc)), (lng1 * B1(inc) + lng3 * B2(inc)) + lng2 * B3(inc) )); inc = inc + (1 / POINT_COUNT); } return curveCoordinates; } /** * 获取弧线的坐标点 */ function getPath() { return this.points; } })(); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet/leaflet.css ================================================ /* required styles */ .leaflet-map-pane, .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-tile-pane, .leaflet-tile-container, .leaflet-overlay-pane, .leaflet-shadow-pane, .leaflet-marker-pane, .leaflet-popup-pane, .leaflet-overlay-pane svg, .leaflet-zoom-box, .leaflet-image-layer, .leaflet-layer { position: absolute; left: 0; top: 0; } .leaflet-container { overflow: hidden; -ms-touch-action: none; } .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow { -webkit-user-select: none; -moz-user-select: none; user-select: none; -webkit-user-drag: none; } .leaflet-marker-icon, .leaflet-marker-shadow { display: block; } /* map is broken in FF if you have max-width: 100% on tiles */ .leaflet-container img { max-width: none !important; } /* stupid Android 2 doesn't understand "max-width: none" properly */ .leaflet-container img.leaflet-image-layer { max-width: 15000px !important; } .leaflet-tile { filter: inherit; visibility: hidden; } .leaflet-tile-loaded { visibility: inherit; } .leaflet-zoom-box { width: 0; height: 0; } /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ .leaflet-overlay-pane svg { -moz-user-select: none; } .leaflet-tile-pane { z-index: 2; } .leaflet-objects-pane { z-index: 3; } .leaflet-overlay-pane { z-index: 4; } .leaflet-shadow-pane { z-index: 5; } .leaflet-marker-pane { z-index: 6; } .leaflet-popup-pane { z-index: 7; } .leaflet-vml-shape { width: 1px; height: 1px; } .lvml { behavior: url(#default#VML); display: inline-block; position: absolute; } /* control positioning */ .leaflet-control { position: relative; z-index: 7; pointer-events: auto; } .leaflet-top, .leaflet-bottom { position: absolute; z-index: 1000; pointer-events: none; } .leaflet-top { top: 0; } .leaflet-right { right: 0; } .leaflet-bottom { bottom: 0; } .leaflet-left { left: 0; } .leaflet-control { float: left; clear: both; } .leaflet-right .leaflet-control { float: right; } .leaflet-top .leaflet-control { margin-top: 10px; } .leaflet-bottom .leaflet-control { margin-bottom: 10px; } .leaflet-left .leaflet-control { margin-left: 10px; } .leaflet-right .leaflet-control { margin-right: 10px; } /* zoom and fade animations */ .leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; -moz-transition: opacity 0.2s linear; -o-transition: opacity 0.2s linear; transition: opacity 0.2s linear; } .leaflet-fade-anim .leaflet-tile-loaded, .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); transition: transform 0.25s cubic-bezier(0,0,0.25,1); } .leaflet-zoom-anim .leaflet-tile, .leaflet-pan-anim .leaflet-tile, .leaflet-touching .leaflet-zoom-animated { -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; } .leaflet-zoom-anim .leaflet-zoom-hide { visibility: hidden; } /* cursors */ .leaflet-clickable { cursor: pointer; } .leaflet-container { cursor: -webkit-grab; cursor: -moz-grab; } .leaflet-popup-pane, .leaflet-control { cursor: auto; } .leaflet-dragging .leaflet-container, .leaflet-dragging .leaflet-clickable { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; } /* visual tweaks */ .leaflet-container { background: #ddd; outline: 0; } .leaflet-container a { color: #0078A8; } .leaflet-container a.leaflet-active { outline: 2px solid orange; } .leaflet-zoom-box { border: 2px dotted #38f; background: rgba(255,255,255,0.5); } /* general typography */ .leaflet-container { font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; } /* general toolbar styles */ .leaflet-bar { box-shadow: 0 1px 5px rgba(0,0,0,0.65); border-radius: 4px; } .leaflet-bar a, .leaflet-bar a:hover { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; height: 26px; line-height: 26px; display: block; text-align: center; text-decoration: none; color: black; } .leaflet-bar a, .leaflet-control-layers-toggle { background-position: 50% 50%; background-repeat: no-repeat; display: block; } .leaflet-bar a:hover { background-color: #f4f4f4; } .leaflet-bar a:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .leaflet-bar a:last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-bottom: none; } .leaflet-bar a.leaflet-disabled { cursor: default; background-color: #f4f4f4; color: #bbb; } .leaflet-touch .leaflet-bar a { width: 30px; height: 30px; line-height: 30px; } /* zoom control */ .leaflet-control-zoom-in, .leaflet-control-zoom-out { font: bold 18px 'Lucida Console', Monaco, monospace; text-indent: 1px; } .leaflet-control-zoom-out { font-size: 20px; } .leaflet-touch .leaflet-control-zoom-in { font-size: 22px; } .leaflet-touch .leaflet-control-zoom-out { font-size: 24px; } /* layers control */ .leaflet-control-layers { box-shadow: 0 1px 5px rgba(0,0,0,0.4); background: #fff; border-radius: 5px; } .leaflet-control-layers-toggle { background-image: url(images/layers.png); width: 36px; height: 36px; } .leaflet-retina .leaflet-control-layers-toggle { background-image: url(images/layers-2x.png); background-size: 26px 26px; } .leaflet-touch .leaflet-control-layers-toggle { width: 44px; height: 44px; } .leaflet-control-layers .leaflet-control-layers-list, .leaflet-control-layers-expanded .leaflet-control-layers-toggle { display: none; } .leaflet-control-layers-expanded .leaflet-control-layers-list { display: block; position: relative; } .leaflet-control-layers-expanded { padding: 6px 10px 6px 6px; color: #333; background: #fff; } .leaflet-control-layers-selector { margin-top: 2px; position: relative; top: 1px; } .leaflet-control-layers label { display: block; } .leaflet-control-layers-separator { height: 0; border-top: 1px solid #ddd; margin: 5px -10px 5px -6px; } /* attribution and scale controls */ .leaflet-container .leaflet-control-attribution { background: #fff; background: rgba(255, 255, 255, 0.7); margin: 0; } .leaflet-control-attribution, .leaflet-control-scale-line { padding: 0 5px; color: #333; } .leaflet-control-attribution a { text-decoration: none; } .leaflet-control-attribution a:hover { text-decoration: underline; } .leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale { font-size: 11px; } .leaflet-left .leaflet-control-scale { margin-left: 5px; } .leaflet-bottom .leaflet-control-scale { margin-bottom: 5px; } .leaflet-control-scale-line { border: 2px solid #777; border-top: none; line-height: 1.1; padding: 2px 5px 1px; font-size: 11px; white-space: nowrap; overflow: hidden; -moz-box-sizing: content-box; box-sizing: content-box; background: #fff; background: rgba(255, 255, 255, 0.5); } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; } .leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; } .leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { box-shadow: none; } .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { border: 2px solid rgba(0,0,0,0.2); background-clip: padding-box; } /* popup */ .leaflet-popup { position: absolute; text-align: center; } .leaflet-popup-content-wrapper { padding: 1px; text-align: left; border-radius: 12px; } .leaflet-popup-content { margin: 13px 19px; line-height: 1.4; } .leaflet-popup-content p { margin: 18px 0; } .leaflet-popup-tip-container { margin: 0 auto; width: 40px; height: 20px; position: relative; overflow: hidden; } .leaflet-popup-tip { width: 17px; height: 17px; padding: 1px; margin: -10px auto 0; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg); } .leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white; box-shadow: 0 3px 14px rgba(0,0,0,0.4); } .leaflet-container a.leaflet-popup-close-button { position: absolute; top: 0; right: 0; padding: 4px 4px 0 0; text-align: center; width: 18px; height: 14px; font: 16px/14px Tahoma, Verdana, sans-serif; color: #c3c3c3; text-decoration: none; font-weight: bold; background: transparent; } .leaflet-container a.leaflet-popup-close-button:hover { color: #999; } .leaflet-popup-scrolled { overflow: auto; border-bottom: 1px solid #ddd; border-top: 1px solid #ddd; } .leaflet-oldie .leaflet-popup-content-wrapper { zoom: 1; } .leaflet-oldie .leaflet-popup-tip { width: 24px; margin: 0 auto; -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); } .leaflet-oldie .leaflet-popup-tip-container { margin-top: -1px; } .leaflet-oldie .leaflet-control-zoom, .leaflet-oldie .leaflet-control-layers, .leaflet-oldie .leaflet-popup-content-wrapper, .leaflet-oldie .leaflet-popup-tip { border: 1px solid #999; } /* div icon */ .leaflet-div-icon { background: #fff; border: 1px solid #666; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet/leaflet.functionaltilelayer.js ================================================ L.TileLayer.Functional = L.TileLayer.extend({ _tileFunction: null, initialize: function (tileFunction, options) { this._tileFunction = tileFunction; L.Util.setOptions(this, options); }, getTileUrl: function (tilePoint) { // Note: bbox code untested; pulled from TileLayer.WMS var map = this._map, crs = map.options.crs, tileSize = this.options.tileSize, zoom = this._map.getZoom(), nwPoint = tilePoint.multiplyBy(tileSize), sePoint = nwPoint.add(new L.Point(tileSize, tileSize)), nw = crs.project(map.unproject(nwPoint, zoom)), se = crs.project(map.unproject(sePoint, zoom)), bbox = [nw.x, se.y, se.x, nw.y].join(','), view = { bbox: bbox, width: tileSize, height: tileSize, zoom: zoom, tile: { row: tilePoint.y, column: tilePoint.x }, subdomain: this._getSubdomain(tilePoint) }; return this._tileFunction(view); }, _loadTile: function (tile, tilePoint) { var tileUrl = this.getTileUrl(tilePoint); tile._layer = this; tile.onload = this._tileOnLoad; tile.onerror = this._tileOnError; if (typeof tileUrl === "string") { tile.src = tileUrl; } else if (tileUrl) { // assume jQuery.Deferred tileUrl.done(function (tileUrl) { tile.src = tileUrl; }); } } }); L.tileLayer.functional = function (tileFunction, options) { return new L.TileLayer.Functional(tileFunction, options); }; ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet/leaflet.js ================================================ /* Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com (c) 2010-2013, Vladimir Agafonkin (c) 2010-2011, CloudMade */ !function(t,e,i){var n=t.L,o={};o.version="0.7-dev","object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define(o),o.noConflict=function(){return t.L=n,this},t.L=o,o.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:function(t,e,i){var n,o;if("object"==typeof t){o=Array.prototype.slice.call(arguments,3);for(n in t)e.apply(i,[n,t[n]].concat(o));return!0}return!1},limitExecByInterval:function(t,e,i){var n,o;return function s(){var a=arguments;return n?(o=!0,void 0):(n=!0,setTimeout(function(){n=!1,o&&(s.apply(i,a),o=!1)},e),t.apply(i,a),void 0)}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){return t.options=o.extend({},t.options,e),t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&-1!==e.indexOf("?")?"&":"?")+n.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,n){var o=e[n];if(o===i)throw new Error("No value provided for variable "+t);return"function"==typeof o&&(o=o(e)),o})},isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;it;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){o.extend(this.prototype,t)},o.Class.mergeOptions=function(t){o.extend(this.prototype.options,t)},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";o.Mixin={},o.Mixin.Events={addEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d=this[s]=this[s]||{},p=i&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)r={action:e,context:i||this},h=t[n],i?(l=h+"_idx",u=l+"_len",c=d[l]=d[l]||{},c[p]||(c[p]=[],d[u]=(d[u]||0)+1),c[p].push(r)):(d[h]=d[h]||[],d[h].push(r));return this},hasEventListeners:function(t){var e=this[s];return!!e&&(t in e&&e[t].length>0||t+"_idx"in e&&e[t+"_idx_len"]>0)},removeEventListener:function(t,e,i){if(!this[s])return this;if(!t)return this.clearAllEventListeners();if(o.Util.invokeEach(t,this.removeEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d,p,_=this[s],m=i&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)if(r=t[n],u=r+"_idx",c=u+"_len",d=_[u],e){if(h=i&&d?d[m]:_[r]){for(l=h.length-1;l>=0;l--)h[l].action!==e||i&&h[l].context!==i||(p=h.splice(l,1),p[0].action=o.Util.falseFn);i&&d&&0===h.length&&(delete d[m],_[c]--)}}else delete _[r],delete _[u];return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;var i,n,a,r,h,l=o.Util.extend({},e,{type:t,target:this}),u=this[s];if(u[t])for(i=u[t].slice(),n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context||this,l);r=u[t+"_idx"];for(h in r)if(i=r[h].slice())for(n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context||this,l);return this},addOneTimeEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addOneTimeEventListener,this,e,i))return this;var n=o.bind(function(){this.removeEventListener(t,e,i).removeEventListener(t,n,i)},this);return this.addEventListener(t,e,i).addEventListener(t,n,i)}},o.Mixin.Events.on=o.Mixin.Events.addEventListener,o.Mixin.Events.off=o.Mixin.Events.removeEventListener,o.Mixin.Events.once=o.Mixin.Events.addOneTimeEventListener,o.Mixin.Events.fire=o.Mixin.Events.fireEvent,function(){var n=!!t.ActiveXObject,s=n&&!t.XMLHttpRequest,a=n&&!e.querySelector,r=n&&!e.addEventListener,h=navigator.userAgent.toLowerCase(),l=-1!==h.indexOf("webkit"),u=-1!==h.indexOf("chrome"),c=-1!==h.indexOf("phantom"),d=-1!==h.indexOf("android"),p=-1!==h.search("android [23]"),_=typeof orientation!=i+"",m=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints,f="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,g=e.documentElement,v=n&&"transition"in g.style,y="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix,L="MozPerspective"in g.style,P="OTransition"in g.style,x=!t.L_DISABLE_3D&&(v||y||L||P)&&!c,w=!t.L_NO_TOUCH&&!c&&function(){var t="ontouchstart";if(m||t in g)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();o.Browser={ie:n,ie6:s,ie7:a,ielt9:r,webkit:l,android:d,android23:p,chrome:u,ie3d:v,webkit3d:y,gecko3d:L,opera3d:P,any3d:x,mobile:_,mobileWebkit:_&&l,mobileWebkit3d:_&&y,mobileOpera:_&&t.opera,touch:w,msTouch:m,retina:f}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,a=s.x>=e.x&&n.x<=i.x,r=s.y>=e.y&&n.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,n=0,s=0,a=t,r=e.body,h=e.documentElement,l=o.Browser.ie7;do{if(n+=a.offsetTop||0,s+=a.offsetLeft||0,n+=parseInt(o.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(o.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=o.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){n+=r.scrollTop||h.scrollTop||0,s+=r.scrollLeft||h.scrollLeft||0;break}if("relative"===i&&!a.offsetLeft){var u=o.DomUtil.getStyle(a,"width"),c=o.DomUtil.getStyle(a,"max-width"),d=a.getBoundingClientRect();("none"!==u||"none"!==c)&&(s+=d.left+a.clientLeft),n+=d.top+(r.scrollTop||h.scrollTop||0);break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;n-=a.scrollTop||0,s-=a.scrollLeft||0,o.DomUtil.documentIsLtr()||!o.Browser.webkit&&!l||(s+=a.scrollWidth-a.clientWidth,l&&"hidden"!==o.DomUtil.getStyle(a,"overflow-y")&&"hidden"!==o.DomUtil.getStyle(a,"overflow")&&(s+=17)),a=a.parentNode}while(a);return new o.Point(s,n)},documentIsLtr:function(){return o.DomUtil._docIsLtrCached||(o.DomUtil._docIsLtrCached=!0,o.DomUtil._docIsLtr="ltr"===o.DomUtil.getStyle(e.body,"direction")),o.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},hasClass:function(t,e){return t.className.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className)},addClass:function(t,e){o.DomUtil.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(t,e){t.className=o.Util.trim((" "+t.className+" ").replace(" "+e+" "," "))},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;ni||i===e?e:t),new o.LatLng(this.lat,i)}},o.latLng=function(t,e){return t instanceof o.LatLng?t:o.Util.isArray(t)?new o.LatLng(t[0],t[1]):t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon):new o.LatLng(t,e)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){return t?(t="number"==typeof t[0]||"string"==typeof t[0]||t instanceof o.LatLng?o.latLng(t):o.latLngBounds(t),t instanceof o.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new o.LatLng(t.lat,t.lng),this._northEast=new o.LatLng(t.lat,t.lng)):t instanceof o.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this):this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof o.LatLng?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&n.lat<=i.lat,r=s.lng>=e.lng&&n.lng<=i.lng;return a&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return t?(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return!t||t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=n*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new o.Point(s,a)},unproject:function(t){var e=o.LatLng.RAD_TO_DEG,i=t.x*e,n=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new o.LatLng(n,i)}},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)}},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),o.CRS.EPSG3857=o.extend({},o.CRS,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:new o.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/360,.5,-1/360,.5)}),o.Map=o.Class.extend({includes:o.Mixin.Events,options:{crs:o.CRS.EPSG3857,fadeAnimation:o.DomUtil.TRANSITION&&!o.Browser.android23,trackResize:!0,markerZoomAnimation:o.DomUtil.TRANSITION&&o.Browser.any3d},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(e.layers)},setView:function(t,e){return this._resetView(o.latLng(t),this._limitZoom(e)),this},setZoom:function(t,e){return this.setView(this.getCenter(),t,{zoom:e})},zoomIn:function(t,e){return this.setZoom(this._zoom+(t||1),e)},zoomOut:function(t,e){return this.setZoom(this._zoom-(t||1),e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),a=t instanceof o.Point?t:this.latLngToContainerPoint(t),r=a.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(r));return this.setView(h,e,{zoom:i})},fitBounds:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n)),a=n.subtract(i).divideBy(2),r=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(r.add(h).divideBy(2).add(a),s);return this.setView(l,s,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t){return this.fire("movestart"),this._rawPanBy(o.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t,e){if(t=o.latLngBounds(t),this.options.maxBounds=t,!t)return this._boundsMinZoom=null,this.off("moveend",this._panInsideMaxBounds,this),this;var i=this.getBoundsZoom(t,!0);return this._boundsMinZoom=i,this._loaded&&(this._zooma.x&&(r=Math.floor(a.x-n.x)),i.y>s.y&&(h=Math.floor(s.y-i.y)),i.x=s);return u&&e?null:e?s:s-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new o.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new o.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet)throw new Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(this.options.fadeAnimation?" leaflet-fade-anim":""));var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,e),o.DomUtil.addClass(t.shadowPane,e),o.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return o.DomUtil.create("div",t,e||this._panes.objectsPane)},_clearPanes:function(){this._container.removeChild(this._mapPane)},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,n){var s=this._zoom!==e;n||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialCenter=t,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,a&&(this.fire("load"),this.eachLayer(this._layerAdd,this)),this.fire("viewreset",{hard:!i}),this.fire("move"),(s||n)&&this.fire("zoomend"),this.fire("moveend",{hard:!i})},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_updateZoomLevels:function(){var t,e=1/0,n=-1/0,o=this._getZoomSpan();for(t in this._zoomBoundLayers){var s=this._zoomBoundLayers[t];isNaN(s.options.minZoom)||(e=Math.min(e,s.options.minZoom)),isNaN(s.options.maxZoom)||(n=Math.max(n,s.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e),o!==this._getZoomSpan()&&this.fire("zoomlevelschange")},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){e=e||"on",o.DomEvent[e](this._container,"click",this._onMouseClick,this);var i,n,s=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(i=0,n=s.length;n>i;i++)o.DomEvent[e](this._container,s[i],this._fireMouseEvent,this);this.options.trackResize&&o.DomEvent[e](t,"resize",this._onResize,this)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(this.invalidateSize,this,!1,this._container)},_onMouseClick:function(t){!this._loaded||!t._simulated&&this.dragging&&this.dragging.moved()||o.DomEvent._skipped(t)||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&o.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),n=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(n);this.fire(e,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this.fire("tilelayersload")},_clearHandlers:function(){for(var t=0,e=this._handlers.length;e>t;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_layerAdd:function(t){t.onAdd(this),this.fire("layeradd",{layer:t})},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(){return this.getPixelOrigin().subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),o.map=function(t,e){return new o.Map(t,e)},o.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=n*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var d=Math.tan(.5*(.5*Math.PI-h))/c;return h=-s*Math.log(d),new o.Point(r,h)},unproject:function(t){for(var e,i=o.LatLng.RAD_TO_DEG,n=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/n,r=s/n,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/n),u=Math.PI/2-2*Math.atan(l),c=15,d=1e-7,p=c,_=.1;Math.abs(_)>d&&--p>0;)e=h*Math.sin(u),_=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=_;return new o.LatLng(u*i,a)}},o.CRS.EPSG3395=o.extend({},o.CRS,{code:"EPSG:3395",projection:o.Projection.Mercator,transformation:function(){var t=o.Projection.Mercator,e=t.R_MAJOR,i=t.R_MINOR;return new o.Transformation(.5/(Math.PI*e),.5,-.5/(Math.PI*i),.5)}()}),o.TileLayer=o.Class.extend({includes:o.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:o.Browser.mobile,updateWhenIdle:o.Browser.mobile},initialize:function(t,e){e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),e.bounds&&(e.bounds=o.latLngBounds(e.bounds)),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._animated=t._zoomAnimated,this._initContainer(),t.on({viewreset:this._reset,moveend:this._update},this),this._animated&&t.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=o.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update() },addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._reset,moveend:this._update},this),this._animated&&t.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){var t,e=this._tiles;if(o.Browser.ielt9)for(t in e)o.DomUtil.setOpacity(e[t],this.options.opacity);else o.DomUtil.setOpacity(this._container,this.options.opacity)},_initContainer:function(){var t=this._map._panes.tilePane;if(!this._container){if(this._container=o.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),this._animated){var e="leaflet-tile-container leaflet-zoom-animated";this._bgBuffer=o.DomUtil.create("div",e,this._container),this._tileContainer=o.DomUtil.create("div",e,this._container)}else this._tileContainer=this._container;t.appendChild(this._container),this.options.opacity<1&&this._updateOpacity()}},_reset:function(t){for(var e in this._tiles)this.fire("tileunload",{tile:this._tiles[e]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),this._tileContainer.innerHTML="",this._animated&&t&&t.hard&&this._clearBgBuffer(),this._initContainer()},_getTileSize:function(){var t=this._map,e=t.getZoom(),i=this.options.maxNativeZoom,n=this.options.tileSize;return i&&e>i&&(n=Math.round(t.getZoomScale(e)/t.getZoomScale(i)*n)),n},_update:function(){if(this._map){var t=this._map,e=t.getPixelBounds(),i=t.getZoom(),n=this._getTileSize();if(!(i>this.options.maxZoom||in;n++)this._addTile(a[n],l);this._tileContainer.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;var e=this.options;if(!e.continuousWorld){var i=this._getWrapTileNum();if(e.noWrap&&(t.x<0||t.x>=i)||t.y<0||t.y>=i)return!1}if(e.bounds){var n=e.tileSize,o=t.multiplyBy(n),s=o.add([n,n]),a=this._map.unproject(o),r=this._map.unproject(s);if(e.continuousWorld||e.noWrap||(a=a.wrap(),r=r.wrap()),!e.bounds.intersects([a,r]))return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(it.max.x||nt.max.y)&&this._removeTile(o)},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(o.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._tileContainer&&this._tileContainer.removeChild(e),o.Browser.android||(e.onload=null,e.src=o.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),n=this._getTile();o.DomUtil.setPosition(n,i,o.Browser.chrome||o.Browser.android23),this._tiles[t.x+":"+t.y]=n,this._loadTile(n,t),n.parentNode!==this._tileContainer&&e.appendChild(n)},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+=t.zoomOffset,t.maxNativeZoom?Math.min(e,t.maxNativeZoom):e},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this._getTileSize();return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return o.Util.template(this._url,o.extend({s:this._getSubdomain(t),z:t.z,x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e+e)%e),this.options.tms&&(t.y=e-t.y-1),t.z=this._getZoomForUrl()},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=o.DomUtil.create("img","leaflet-tile");return t.style.width=t.style.height=this._getTileSize()+"px",t.galleryimg="no",t.onselectstart=t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity!==i&&o.DomUtil.setOpacity(t,this.options.opacity),t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,this._adjustTilePoint(e),t.src=this.getTileUrl(e)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(o.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var t=this._layer;this.src!==o.Util.emptyImageUrl&&(o.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams),n=e.tileSize||this.options.tileSize;i.width=i.height=e.detectRetina&&o.Browser.retina?2*n:n;for(var s in e)this.options.hasOwnProperty(s)||"crs"===s||(i[s]=e[s]);this.wmsParams=i,o.setOptions(this,e)},onAdd:function(t){this._crs=this.options.crs||t.options.crs;var e=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._map,i=this.options.tileSize,n=t.multiplyBy(i),s=n.add([i,i]),a=this._crs.project(e.unproject(n,t.z)),r=this._crs.project(e.unproject(s,t.z)),h=[a.x,r.y,r.x,a.y].join(","),l=o.Util.template(this._url,{s:this._getSubdomain(t)});return l+o.Util.getParamString(this.wmsParams,l,!0)+"&BBOX="+h},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.TileLayer.Canvas=o.TileLayer.extend({options:{async:!1},initialize:function(t){o.setOptions(this,t)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var t in this._tiles)this._redrawTile(this._tiles[t]);return this},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTile:function(){var t=o.DomUtil.create("canvas","leaflet-tile");return t.width=t.height=this.options.tileSize,t.onselectstart=t.onmousemove=o.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),o.tileLayer.canvas=function(t){return new o.TileLayer.Canvas(t)},o.ImageOverlay=o.Class.extend({includes:o.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&o.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},_initImage:function(){this._image=o.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&o.Browser.any3d?o.DomUtil.addClass(this._image,"leaflet-zoom-animated"):o.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),o.extend(this._image,{galleryimg:"no",onselectstart:o.Util.falseFn,onmousemove:o.Util.falseFn,onload:o.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,n=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/n)));i.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(l)+" scale("+n+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);o.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({options:{className:""},initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n;return n=e&&"IMG"===e.tagName?this._createImg(i,e):this._createImg(i),this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i,n=this.options,s=o.point(n[e+"Size"]);i="shadow"===e?o.point(n.shadowAnchor||n.iconAnchor):o.point(n.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+n.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return o.Browser.ie6?(i||(i=e.createElement("div")),i.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+t+'")'):(i||(i=e.createElement("img")),i.src=t),i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];o.Browser.retina&&"icon"===t&&(t+="-2x");var i=o.Icon.Default.imagePath;if(!i)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),o.Icon.Default.imagePath=function(){var t,i,n,o,s,a=e.getElementsByTagName("script"),r=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=a.length;i>t;t++)if(n=a[t].src,o=n.match(r))return s=n.split(r)[0],(s?s+"/":"")+"images"}(),o.Marker=o.Class.extend({includes:o.Mixin.Events,options:{icon:new o.Icon.Default,title:"",clickable:!0,draggable:!1,keyboard:!0,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this.dragging&&this.dragging.disable(),this._removeIcon(),this._removeShadow(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,n=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=t.icon.createIcon(this._icon),a=!1;s!==this._icon&&(this._icon&&this._removeIcon(),a=!0,t.title&&(s.title=t.title)),o.DomUtil.addClass(s,n),t.keyboard&&(s.tabIndex="0"),this._icon=s,this._initInteraction(),t.riseOnHover&&o.DomEvent.on(s,"mouseover",this._bringToFront,this).on(s,"mouseout",this._resetZIndex,this);var r=t.icon.createShadow(this._shadow),h=!1;r!==this._shadow&&(this._removeShadow(),h=!0),r&&o.DomUtil.addClass(r,n),this._shadow=r,t.opacity<1&&this._updateOpacity();var l=this._map._panes;a&&l.markerPane.appendChild(this._icon),r&&h&&l.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&o.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),this._map._panes.markerPane.removeChild(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];o.DomUtil.addClass(t,"leaflet-clickable"),o.DomEvent.on(t,"click",this._onMouseClick,this),o.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;is?(e.height=s+"px",o.DomUtil.addClass(t,a)):o.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=o.point(this.options.offset);e&&o.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,n=new o.Point(this._containerLeft,-e-this._containerBottom);this._animated&&n._add(o.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(n),a=o.point(this.options.autoPanPadding),r=t.getSize(),h=0,l=0;s.x+i>r.x&&(h=s.x+i-r.x+a.x),s.x-h<0&&(h=s.x-a.x),s.y+e>r.y&&(l=s.y+e-r.y+a.y),s.y-l<0&&(l=s.y-a.y),(h||l)&&t.fire("autopanstart").panBy([h,l])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.include({openPopup:function(t,e,i){if(this.closePopup(),!(t instanceof o.Popup)){var n=t;t=new o.Popup(i).setLatLng(e).setContent(n)}return t._isOpen=!0,this._popup=t,this.addLayer(t)},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&(this.removeLayer(t),t._isOpen=!1),this}}),o.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(t,e){var i=o.point(this.options.icon.options.popupAnchor||[0,0]);return i=i.add(o.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=o.extend({offset:i},e),this._popup||this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),t instanceof o.Popup?(o.setOptions(t,e),this._popup=t):this._popup=new o.Popup(e,this).setContent(t),this},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup).off("remove",this.closePopup).off("move",this._movePopup)),this},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.LayerGroup=o.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return t?t in this._layers||this.getLayerId(t)in this._layers:!1},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({includes:o.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose"},addLayer:function(t){return this.hasLayer(t)?this:(t.on(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.off(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})):this},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new o.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof o.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t.layer||(t.layer=t.target),t.target=this,this.fire(t.type,t)}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Path=o.Class.extend({includes:[o.Mixin.Events],statics:{CLIP_PADDING:function(){var e=o.Browser.mobile?1280:2e3,i=(e/Math.max(t.outerWidth,t.outerHeight)-1)/2;return Math.max(0,Math.min(.5,i))}()},options:{stroke:!0,color:"#0033ff",dashArray:null,lineCap:null,lineJoin:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){o.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,o.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return o.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),o.Map.include({_updatePathViewport:function(){var t=o.Path.CLIP_PADDING,e=this.getSize(),i=o.DomUtil.getPosition(this._mapPane),n=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=n.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new o.Bounds(n,s)}}),o.Path.SVG_NS="http://www.w3.org/2000/svg",o.Browser.svg=!(!e.createElementNS||!e.createElementNS(o.Path.SVG_NS,"svg").createSVGRect),o.Path=o.Path.extend({statics:{SVG:o.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(o.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this.options.pointerEvents&&this._path.setAttribute("pointer-events",this.options.pointerEvents),this.options.clickable||this.options.pointerEvents||this._path.setAttribute("pointer-events","none"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray"),this.options.lineCap&&this._path.setAttribute("stroke-linecap",this.options.lineCap),this.options.lineJoin&&this._path.setAttribute("stroke-linejoin",this.options.lineJoin)):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(o.Browser.svg||!o.Browser.vml)&&this._path.setAttribute("class","leaflet-clickable"),o.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;e';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),o.Path=o.Browser.svg||!o.Browser.vml?o.Path:o.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("') }}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");o.DomUtil.addClass(t,"leaflet-vml-shape"),this.options.clickable&&o.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?i.dashArray instanceof Array?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):"",i.lineCap&&(t.endcap=i.lineCap.replace("butt","flat")),i.lineJoin&&(t.joinstyle=i.lineJoin)):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),o.Map.include(o.Browser.svg||!o.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.Path=o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?o.Path:o.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return o.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!o.Path._updateRequest&&(o.Path._updateRequest=o.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){o.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,n,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,n=this._parts[t].length;n>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof o.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&(this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onClick,this))},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",t)},_onMouseMove:function(t){this._map&&!this._map._animatingZoom&&(this._containsPoint(t.layerPoint)?(this._ctx.canvas.style.cursor="pointer",this._mouseInside=!0,this.fire("mouseover",t)):this._mouseInside&&(this._ctx.canvas.style.cursor="",this._mouseInside=!1,this.fire("mouseout",t)))}}),o.Map.include(o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),n=this._pathRoot;o.DomUtil.setPosition(n,e),n.width=i.x,n.height=i.y,n.getContext("2d").translate(-e.x,-e.y)}}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,i,n){var s=e.x-t.x,a=e.y-t.y,r=n.min,h=n.max;return 8&i?new o.Point(t.x+s*(h.y-t.y)/a,h.y):4&i?new o.Point(t.x+s*(r.y-t.y)/a,r.y):2&i?new o.Point(h.x,t.y+a*(h.x-t.x)/s):1&i?new o.Point(r.x,t.y+a*(r.x-t.x)/s):void 0},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,n?h*h+l*l:new o.Point(a,r)}},o.Polyline=o.Path.extend({initialize:function(t,e){o.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(o.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs,!0),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,n=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];var d=o.LineUtil._sqClosestPointOnSegment(t,e,i,!0);n>d&&(n=d,a=o.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(n)),a},getBounds:function(){return new o.LatLngBounds(this.getLatLngs())},_convertLatLngs:function(t,e){var i,n,s=e?t:[];for(i=0,n=t.length;n>i;i++){if(o.Util.isArray(t[i])&&"number"!=typeof t[i][0])return;s[i]=o.latLng(t[i])}return s},_initEvents:function(){o.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=o.Path.VML,n=0,s=t.length,a="";s>n;n++)e=t[n],i&&e._round(),a+=(n?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,i,n=this._originalPoints,s=n.length;if(this.options.noClip)return this._parts=[n],void 0;this._parts=[];var a=this._parts,r=this._map._pathViewport,h=o.LineUtil;for(t=0,e=0;s-1>t;t++)i=h.clipSegment(n[t],n[t+1],r,t),i&&(a[e]=a[e]||[],a[e].push(i[0]),(i[1]!==n[t+1]||t===s-2)&&(a[e].push(i[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=o.LineUtil,i=0,n=t.length;n>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),o.Path.prototype._updatePath.call(this))}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e){var i,n,s,a,r,h,l,u,c,d=[1,4,2,8],p=o.LineUtil;for(n=0,l=t.length;l>n;n++)t[n]._code=p._getBitCode(t[n],e);for(a=0;4>a;a++){for(u=d[a],i=[],n=0,l=t.length,s=l-1;l>n;s=n++)r=t[n],h=t[s],r._code&u?h._code&u||(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)):(h._code&u&&(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},initialize:function(t,e){var i,n,s;if(o.Polyline.prototype.initialize.call(this,t,e),t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0])for(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1),i=0,n=this._holes.length;n>i;i++)s=this._holes[i]=this._convertLatLngs(this._holes[i]),s[0].equals(s[s.length-1])&&s.pop();t=this._latlngs,t.length>=2&&t[0].equals(t[t.length-1])&&t.pop()},projectLatlngs:function(){if(o.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,n;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,n=this._holes[t].length;n>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,n=this._parts.length;n>i;i++){var s=o.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=o.Polyline.prototype._getPathPartStr.call(this,t);return e+(o.Browser.svg?"z":"x")}}),o.polygon=function(t,e){return new o.Polygon(t,e)},function(){function t(t){return o.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this},getLatLngs:function(){var t=[];return this.eachLayer(function(e){t.push(e.getLatLngs())}),t}})}o.MultiPolyline=t(o.Polyline),o.MultiPolygon=t(o.Polygon),o.multiPolyline=function(t,e){return new o.MultiPolyline(t,e)},o.multiPolygon=function(t,e){return new o.MultiPolygon(t,e)}}(),o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.Circle=o.Path.extend({initialize:function(t,e,i){o.Path.prototype.initialize.call(this,i),this._latlng=o.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=this._latlng,i=this._map.latLngToLayerPoint([e.lat,e.lng-t]);this._point=this._map.latLngToLayerPoint(e),this._radius=Math.max(this._point.x-i.x,1)},getBounds:function(){var t=this._getLngRadius(),e=360*(this._mRadius/40075017),i=this._latlng;return new o.LatLngBounds([i.lat-e,i.lng-t],[i.lat+e,i.lng+t])},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":o.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,"+23592600)},getRadius:function(){return this._mRadius},_getLatRadius:function(){return 360*(this._mRadius/40075017)},_getLngRadius:function(){return this._getLatRadius()/Math.cos(o.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],n=0,r=l.length,s=r-1;r>n;s=n++)if((e||0!==n)&&(h=o.LineUtil.pointToSegmentDistance(t,l[s],l[n]),u>=h))return!0;return!1}}:{}),o.Polygon.include(o.Path.CANVAS?{_containsPoint:function(t){var e,i,n,s,a,r,h,l,u=!1;if(o.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u}}:{}),o.Circle.include(o.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),o.CircleMarker.include(o.Path.CANVAS?{_updateStyle:function(){o.Path.prototype._updateStyle.call(this)}}:{}),o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;i>e;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(s[e]);return this}var a=this.options;if(!a.filter||a.filter(t)){var r=o.GeoJSON.geometryToLayer(t,a.pointToLayer,a.coordsToLatLng);return r.feature=o.GeoJSON.asFeature(t),r.defaultOptions=r.options,this.resetStyle(r),a.onEachFeature&&a.onEachFeature(t,r),this.addLayer(r)}},resetStyle:function(t){var e=this.options.style;e&&(o.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e,i){var n,s,a,r,h,l="Feature"===t.type?t.geometry:t,u=l.coordinates,c=[];switch(i=i||this.coordsToLatLng,l.type){case"Point":return n=i(u),e?e(t,n):new o.Marker(n);case"MultiPoint":for(a=0,r=u.length;r>a;a++)n=i(u[a]),h=e?e(t,n):new o.Marker(n),c.push(h);return new o.FeatureGroup(c);case"LineString":return s=this.coordsToLatLngs(u,0,i),new o.Polyline(s);case"Polygon":if(2===u.length&&!u[1].length)throw new Error("Invalid GeoJSON object.");return s=this.coordsToLatLngs(u,1,i),new o.Polygon(s);case"MultiLineString":return s=this.coordsToLatLngs(u,1,i),new o.MultiPolyline(s);case"MultiPolygon":return s=this.coordsToLatLngs(u,2,i),new o.MultiPolygon(s);case"GeometryCollection":for(a=0,r=l.geometries.length;r>a;a++)h=this.geometryToLayer({geometry:l.geometries[a],type:"Feature",properties:t.properties},e,i),c.push(h);return new o.FeatureGroup(c);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0])},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):(i||this.coordsToLatLng)(t[o]),a.push(n);return a},latLngToCoords:function(t){return[t.lng,t.lat]},latLngsToCoords:function(t){for(var e=[],i=0,n=t.length;n>i;i++)e.push(o.GeoJSON.latLngToCoords(t[i]));return e},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.include({toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"LineString",coordinates:o.GeoJSON.latLngsToCoords(this.getLatLngs())})}}),o.Polygon.include({toGeoJSON:function(){var t,e,i,n=[o.GeoJSON.latLngsToCoords(this.getLatLngs())];if(n[0].push(n[0][0]),this._holes)for(t=0,e=this._holes.length;e>t;t++)i=o.GeoJSON.latLngsToCoords(this._holes[t]),i.push(i[0]),n.push(i);return o.GeoJSON.getFeature(this,{type:"Polygon",coordinates:n})}}),function(){function t(t,e){t.include({toGeoJSON:function(){var t=[];return this.eachLayer(function(e){t.push(e.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:e,coordinates:t})}})}t(o.MultiPolyline,"MultiLineString"),t(o.MultiPolygon,"MultiPolygon")}(),o.LayerGroup.include({toGeoJSON:function(){var t=[];return this.eachLayer(function(e){e.toGeoJSON&&t.push(o.GeoJSON.asFeature(e.toGeoJSON()))}),{type:"FeatureCollection",features:t}}}),o.geoJson=function(t,e){return new o.GeoJSON(t,e)},o.DomEvent={addListener:function(t,e,i,n){var s,a,r,h=o.stamp(i),l="_leaflet_"+e+h;return t[l]?this:(s=function(e){return i.call(n||t,e||o.DomEvent._getEvent())},o.Browser.msTouch&&0===e.indexOf("touch")?this.addMsTouchListener(t,e,s,h):(o.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,s,h),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",s,!1),t.addEventListener(e,s,!1)):"mouseenter"===e||"mouseleave"===e?(a=s,r="mouseenter"===e?"mouseover":"mouseout",s=function(e){return o.DomEvent._checkMouse(t,e)?a(e):void 0},t.addEventListener(r,s,!1)):"click"===e&&o.Browser.android?(a=s,s=function(t){return o.DomEvent._filterClick(t,a)},t.addEventListener(e,s,!1)):t.addEventListener(e,s,!1):"attachEvent"in t&&t.attachEvent("on"+e,s),t[l]=s,this))},removeListener:function(t,e,i){var n=o.stamp(i),s="_leaflet_"+e+n,a=t[s];return a?(o.Browser.msTouch&&0===e.indexOf("touch")?this.removeMsTouchListener(t,e,n):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,n):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,this},disableClickPropagation:function(t){for(var e=o.DomEvent.stopPropagation,i=o.Draggable.START.length-1;i>=0;i--)o.DomEvent.addListener(t,o.Draggable.START[i],e);return o.DomEvent.addListener(t,"click",o.DomEvent._fakeStop).addListener(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,i){var n=o.Browser.ie7,s=e.body,a=e.documentElement,r=t.pageX?t.pageX-s.scrollLeft-a.scrollLeft:t.clientX,h=t.pageY?t.pageY-s.scrollTop-a.scrollTop:t.clientY,l=new o.Point(r,h);if(!i)return l;var u=i.getBoundingClientRect(),c=u.left-i.clientLeft,d=u.top-i.clientTop;return o.DomUtil.documentIsLtr()||!o.Browser.webkit&&!n||(c+=i.scrollWidth-i.clientWidth,n&&"hidden"!==o.DomUtil.getStyle(i,"overflow-y")&&"hidden"!==o.DomUtil.getStyle(i,"overflow")&&(c+=17)),l._subtract(new o.Point(c,d))},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&1e3>n||t.target._simulatedClick&&!t._simulated?(o.DomEvent.stop(t),void 0):(o.DomEvent._lastClick=i,e(t))}},o.DomEvent.on=o.DomEvent.addListener,o.DomEvent.off=o.DomEvent.removeListener,o.Draggable=o.Class.extend({includes:o.Mixin.Events,statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e){this._element=t,this._dragStartTarget=e||t},enable:function(){if(!this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.on(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.off(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(!t.shiftKey&&(1===t.which||1===t.button||t.touches)&&(o.DomEvent.stopPropagation(t),!o.Draggable._disabled)){o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection();var i=t.touches?t.touches[0]:t,n=i.target;o.Browser.touch&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._moved=!1,this._moving||(this._startPoint=new o.Point(i.clientX,i.clientY),this._startPos=this._newPos=o.DomUtil.getPosition(this._element),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this))}},_onMove:function(t){if(!(t.touches&&t.touches.length>1)){var i=t.touches&&1===t.touches.length?t.touches[0]:t,n=new o.Point(i.clientX,i.clientY),s=n.subtract(this._startPoint);(s.x||s.y)&&(o.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(s),o.Browser.touch||o.DomUtil.addClass(e.body,"leaflet-dragging")),this._newPos=this._startPos.add(s),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget))}},_updatePosition:function(){this.fire("predrag"),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(){o.Browser.touch||o.DomUtil.removeClass(e.body,"leaflet-dragging");for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove).off(e,o.Draggable.END[t],this._onUp);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend")),this._moving=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:o.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this),this._onViewReset())}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)e.inertiaThreshold||!this._positions[0];if(t.fire("dragend"),n)t.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),a=(this._lastTime+i-this._times[0])/1e3,r=e.easeLinearity,h=s.multiplyBy(r/a),l=h.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,l),c=h.multiplyBy(u/l),d=u/(e.inertiaDeceleration*r),p=c.multiplyBy(-d/2).round();p.x&&p.y?o.Util.requestAnimFrame(function(){t.panBy(p,{duration:d,easeLinearity:r,noMoveStart:!0})}):t.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(t){this.setZoomAround(t.containerPoint,this._zoom+1)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),o.DomEvent.on(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),o.DomEvent.off(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),i),o.DomEvent.preventDefault(t),o.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();e=e>0?Math.ceil(e):Math.floor(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e&&t.setZoomAround(this._lastMousePos,i+e)}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msTouch?"MSPointerDown":"touchstart",_touchend:o.Browser.msTouch?"MSPointerUp":"touchend",addDoubleTapListener:function(t,i,n){function s(t){var e;if(o.Browser.msTouch?(_.push(t.pointerId),e=_.length):e=t.touches.length,!(e>1)){var i=Date.now(),n=i-(r||i);h=t.touches?t.touches[0]:t,l=n>0&&u>=n,r=i}}function a(t){if(o.Browser.msTouch){var e=_.indexOf(t.pointerId);if(-1===e)return;_.splice(e,1)}if(l){if(o.Browser.msTouch){var n,s={};for(var a in h)n=h[a],s[a]="function"==typeof n?n.bind(h):n;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",d=this._touchstart,p=this._touchend,_=[];t[c+d+n]=s,t[c+p+n]=a;var m=o.Browser.msTouch?e.documentElement:t;return t.addEventListener(d,s,!1),m.addEventListener(p,a,!1),o.Browser.msTouch&&m.addEventListener("MSPointerCancel",a,!1),this},removeDoubleTapListener:function(t,i){var n="_leaflet_";return t.removeEventListener(this._touchstart,t[n+this._touchstart+i],!1),(o.Browser.msTouch?e.documentElement:t).removeEventListener(this._touchend,t[n+this._touchend+i],!1),o.Browser.msTouch&&e.documentElement.removeEventListener("MSPointerCancel",t[n+this._touchend+i],!1),this}}),o.extend(o.DomEvent,{_msTouches:[],_msDocumentListener:!1,addMsTouchListener:function(t,e,i,n){switch(e){case"touchstart":return this.addMsTouchListenerStart(t,e,i,n);case"touchend":return this.addMsTouchListenerEnd(t,e,i,n);case"touchmove":return this.addMsTouchListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addMsTouchListenerStart:function(t,i,n,o){var s="_leaflet_",a=this._msTouches,r=function(t){for(var e=!1,i=0;i0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,clearTimeout(this._holdTimeout),void 0;var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.msTouch&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(t){return!t.shiftKey||1!==t.which&&1!==t.button?!1:(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this),this._map.fire("boxzoomstart"),void 0)},_onMouseMove:function(t){var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._pane.removeChild(this._box),this._container.style.cursor="",o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart"),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-dragging")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(){this._marker.fire("moveend").fire("dragend"),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-dragging")}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton("+","Zoom in",e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton("-","Zoom out",e+"-out",i,this._zoomOut,this),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){return this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container),t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange).off("layerremove",this._onLayerChange)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):(o.DomEvent.disableClickPropagation(e),o.DomEvent.on(e,"mousewheel",o.DomEvent.stopPropagation));var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n='t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,this.fire("step"),void 0):(this._onTransitionEnd(),void 0)},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,!0)},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=this._limitZoom(e),t=o.latLng(t),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return(e&&e.animate)===!0||this.getSize().contains(i)?(this.panBy(i,e),!0):!1}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(){this._animatingZoom&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return i.animate===!0||this.getSize().contains(o)?(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0):!1},_animateZoom:function(t,e,i,n,s,a){this._animatingZoom=!0,o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a})},_onZoomTransitionEnd:function(){this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth),this._animating=!1},_clearBgBuffer:function(){var t=this._map;!t||t._animatingZoom||t.touchZoom._zooming||(this._bgBuffer.innerHTML="",this._bgBuffer.style[o.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var t=this._tileContainer,e=this._bgBuffer,i=this._getLoadedTilesPercentage(e),n=this._getLoadedTilesPercentage(t);return e&&i>.5&&.5>n?(t.style.visibility="hidden",this._stopLoadingImages(t),void 0):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),clearTimeout(this._clearBgBufferTimer),void 0)},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet/leaflet.markercluster.js ================================================ /* Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. https://github.com/Leaflet/Leaflet.markercluster (c) 2012-2013, Dave Leaver, smartrak */ !function(t,e){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,polygonOptions:{}},initialize:function(t){L.Util.setOptions(this,t),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.on(L.FeatureGroup.EVENTS,this._propagateEvent,this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.on(L.FeatureGroup.EVENTS,this._propagateEvent,this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null},addLayer:function(t){if(t instanceof L.LayerGroup){var e=[];for(var i in t._layers)e.push(t._layers[i]);return this.addLayers(e)}if(!t.getLatLng)return this._nonPointGroup.addLayer(t),this;if(!this._map)return this._needsClustering.push(t),this;if(this.hasLayer(t))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(t,this._maxZoom);var n=t,s=this._map.getZoom();if(t.__parent)for(;n.__parent._zoom>=s;)n=n.__parent;return this._currentShownBounds.contains(n.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(t,n):this._animationAddLayerNonAnimated(t,n)),this},removeLayer:function(t){if(t instanceof L.LayerGroup){var e=[];for(var i in t._layers)e.push(t._layers[i]);return this.removeLayers(e)}return t.getLatLng?this._map?t.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(t)),this._removeLayer(t,!0),this._featureGroup.hasLayer(t)&&(this._featureGroup.removeLayer(t),t.setOpacity&&t.setOpacity(1)),this):this:(!this._arraySplice(this._needsClustering,t)&&this.hasLayer(t)&&this._needsRemoving.push(t),this):(this._nonPointGroup.removeLayer(t),this)},addLayers:function(t){var e,i,n,s=this._map,r=this._featureGroup,o=this._nonPointGroup;for(e=0,i=t.length;i>e;e++)if(n=t[e],n.getLatLng){if(!this.hasLayer(n))if(s){if(this._addLayer(n,this._maxZoom),n.__parent&&2===n.__parent.getChildCount()){var a=n.__parent.getAllChildMarkers(),h=a[0]===n?a[1]:a[0];r.removeLayer(h)}}else this._needsClustering.push(n)}else o.addLayer(n);return s&&(r.eachLayer(function(t){t instanceof L.MarkerCluster&&t._iconNeedsUpdate&&t._updateIcon()}),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)),this},removeLayers:function(t){var e,i,n,s=this._featureGroup,r=this._nonPointGroup;if(!this._map){for(e=0,i=t.length;i>e;e++)n=t[e],this._arraySplice(this._needsClustering,n),r.removeLayer(n);return this}for(e=0,i=t.length;i>e;e++)n=t[e],n.__parent?(this._removeLayer(n,!0,!0),s.hasLayer(n)&&(s.removeLayer(n),n.setOpacity&&n.setOpacity(1))):r.removeLayer(n);return this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),s.eachLayer(function(t){t instanceof L.MarkerCluster&&t._updateIcon()}),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(t){delete t.__parent}),this._map&&this._generateInitialClusters(),this},getBounds:function(){var t=new L.LatLngBounds;if(this._topClusterLevel)t.extend(this._topClusterLevel._bounds);else for(var e=this._needsClustering.length-1;e>=0;e--)t.extend(this._needsClustering[e].getLatLng());var i=this._nonPointGroup.getBounds();return i.isValid()&&t.extend(i),t},eachLayer:function(t,e){var i,n=this._needsClustering.slice();for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(n),i=n.length-1;i>=0;i--)t.call(e,n[i]);this._nonPointGroup.eachLayer(t,e)},hasLayer:function(t){if(!t)return!1;var e,i=this._needsClustering;for(e=i.length-1;e>=0;e--)if(i[e]===t)return!0;for(i=this._needsRemoving,e=i.length-1;e>=0;e--)if(i[e]===t)return!1;return!(!t.__parent||t.__parent._group!==this)||this._nonPointGroup.hasLayer(t)},zoomToShowLayer:function(t,e){var i=function(){if((t._icon||t.__parent._icon)&&!this._inZoomAnimation)if(this._map.off("moveend",i,this),this.off("animationend",i,this),t._icon)e();else if(t.__parent._icon){var n=function(){this.off("spiderfied",n,this),e()};this.on("spiderfied",n,this),t.__parent.spiderfy()}};t._icon?e():t.__parent._zoome;e++)n=this._needsRemoving[e],this._removeLayer(n,!0);for(this._needsRemoving=[],e=0,i=this._needsClustering.length;i>e;e++)n=this._needsClustering[e],n.getLatLng?n.__parent||this._addLayer(n,this._maxZoom):this._featureGroup.addLayer(n);this._needsClustering=[],this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),this._zoom=this._map.getZoom(),this._currentShownBounds=this._getExpandedVisibleBounds(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)},onRemove:function(t){t.off("zoomend",this._zoomEnd,this),t.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),this._featureGroup.onRemove(t),this._nonPointGroup.onRemove(t),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(t){for(var e=t;null!==e&&!e._icon;)e=e.__parent;return e},_arraySplice:function(t,e){for(var i=t.length-1;i>=0;i--)if(t[i]===e)return t.splice(i,1),!0},_removeLayer:function(t,e,i){var n=this._gridClusters,s=this._gridUnclustered,r=this._featureGroup,o=this._map;if(e)for(var a=this._maxZoom;a>=0&&s[a].removeObject(t,o.project(t.getLatLng(),a));a--);var h,_=t.__parent,u=_._markers;for(this._arraySplice(u,t);_&&(_._childCount--,!(_._zoom<0));)e&&_._childCount<=1?(h=_._markers[0]===t?_._markers[1]:_._markers[0],n[_._zoom].removeObject(_,o.project(_._cLatLng,_._zoom)),s[_._zoom].addObject(h,o.project(h.getLatLng(),_._zoom)),this._arraySplice(_.__parent._childClusters,_),_.__parent._markers.push(h),h.__parent=_.__parent,_._icon&&(r.removeLayer(_),i||r.addLayer(h))):(_._recalculateBounds(),i&&_._icon||_._updateIcon()),_=_.__parent;delete t.__parent},_propagateEvent:function(t){t.layer instanceof L.MarkerCluster&&(t.type="cluster"+t.type),this.fire(t.type,t)},_defaultIconCreateFunction:function(t){var e=t.getChildCount(),i=" marker-cluster-";return i+=10>e?"small":100>e?"medium":"large",new L.DivIcon({html:"
                            "+e+"
                            ",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var t=this._map,e=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(e||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),t.on("zoomend",this._hideCoverage,this),t.on("layerremove",this._hideCoverageOnRemove,this))},_zoomOrSpiderfy:function(t){var e=this._map;e.getMaxZoom()===e.getZoom()?this.options.spiderfyOnMaxZoom&&t.layer.spiderfy():this.options.zoomToBoundsOnClick&&t.layer.zoomToBounds()},_showCoverage:function(t){var e=this._map;this._inZoomAnimation||(this._shownPolygon&&e.removeLayer(this._shownPolygon),t.layer.getChildCount()>2&&t.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(t.layer.getConvexHull(),this.options.polygonOptions),e.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_hideCoverageOnRemove:function(t){t.layer===this&&this._hideCoverage()},_unbindEvents:function(){var t=this.options.spiderfyOnMaxZoom,e=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(t||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),e&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this),n.off("layerremove",this._hideCoverageOnRemove,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=this._map._zoom,this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var t=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,this._zoom,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,t),this._currentShownBounds=t}},_generateInitialClusters:function(){var t=this._map.getMaxZoom(),e=this.options.maxClusterRadius;this.options.disableClusteringAtZoom&&(t=this.options.disableClusteringAtZoom-1),this._maxZoom=t,this._gridClusters={},this._gridUnclustered={};for(var i=t;i>=0;i--)this._gridClusters[i]=new L.DistanceGrid(e),this._gridUnclustered[i]=new L.DistanceGrid(e);this._topClusterLevel=new L.MarkerCluster(this,-1)},_addLayer:function(t,e){var i,n,s=this._gridClusters,r=this._gridUnclustered;for(this.options.singleMarkerMode&&(t.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[t]}}));e>=0;e--){i=this._map.project(t.getLatLng(),e);var o=s[e].getNearObject(i);if(o)return o._addChild(t),t.__parent=o,void 0;if(o=r[e].getNearObject(i)){var a=o.__parent;a&&this._removeLayer(o,!1);var h=new L.MarkerCluster(this,e,o,t);s[e].addObject(h,this._map.project(h._cLatLng,e)),o.__parent=h,t.__parent=h;var _=h;for(n=e-1;n>a._zoom;n--)_=new L.MarkerCluster(this,n,_),s[n].addObject(_,this._map.project(o.getLatLng(),n));for(a._addChild(_),n=e;n>=0&&r[n].removeObject(o,this._map.project(o.getLatLng(),n));n--);return}r[e].addObject(t,i)}this._topClusterLevel._addChild(t),t.__parent=this._topClusterLevel},_mergeSplitClusters:function(){this._zoomthis._map._zoom?(this._animationStart(),this._animationZoomOut(this._zoom,this._map._zoom)):this._moveEnd()},_getExpandedVisibleBounds:function(){if(!this.options.removeOutsideVisibleBounds)return this.getBounds();var t=this._map,e=t.getBounds(),i=e._southWest,n=e._northEast,s=L.Browser.mobile?0:Math.abs(i.lat-n.lat),r=L.Browser.mobile?0:Math.abs(i.lng-n.lng);return new L.LatLngBounds(new L.LatLng(i.lat-s,i.lng-r,!0),new L.LatLng(n.lat+s,n.lng+r,!0))},_animationAddLayerNonAnimated:function(t,e){if(e===t)this._featureGroup.addLayer(t);else if(2===e._childCount){e._addToMap();var i=e.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else e._updateIcon()}}),L.MarkerClusterGroup.include(L.DomUtil.TRANSITION?{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_animationZoomIn:function(t,e){var i,n=this,s=this._getExpandedVisibleBounds(),r=this._featureGroup;this._topClusterLevel._recursively(s,t,0,function(n){var o,a=n._latlng,h=n._markers;for(s.contains(a)||(a=null),n._isSingleParent()&&t+1===e?(r.removeLayer(n),n._recursivelyAddChildrenToMap(null,e,s)):(n.setOpacity(0),n._recursivelyAddChildrenToMap(a,e,s)),i=h.length-1;i>=0;i--)o=h[i],s.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),n._topClusterLevel._recursivelyBecomeVisible(s,e),r.eachLayer(function(t){t instanceof L.MarkerCluster||!t._icon||t.setOpacity(1)}),n._topClusterLevel._recursively(s,t,e,function(t){t._recursivelyRestoreChildPositions(e)}),setTimeout(function(){n._topClusterLevel._recursively(s,t,0,function(t){r.removeLayer(t),t.setOpacity(1)}),n._animationEnd()},200)},_animationZoomOut:function(t,e){this._animationZoomOutSingle(this._topClusterLevel,t-1,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t,this._getExpandedVisibleBounds())},_animationZoomOutSingle:function(t,e,i){var n=this._getExpandedVisibleBounds();t._recursivelyAnimateChildrenInAndAddSelfToMap(n,e+1,i);var s=this;this._forceLayout(),t._recursivelyBecomeVisible(n,i),setTimeout(function(){if(1===t._childCount){var r=t._markers[0];r.setLatLng(r.getLatLng()),r.setOpacity(1)}else t._recursively(n,i,0,function(t){t._recursivelyRemoveChildrenFromMap(n,e+1)});s._animationEnd()},200)},_animationAddLayer:function(t,e){var i=this,n=this._featureGroup;n.addLayer(t),e!==t&&(e._childCount>2?(e._updateIcon(),this._forceLayout(),this._animationStart(),t._setPos(this._map.latLngToLayerPoint(e.getLatLng())),t.setOpacity(0),setTimeout(function(){n.removeLayer(t),t.setOpacity(1),i._animationEnd()},200)):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(e,this._map.getMaxZoom(),this._map.getZoom())))},_forceLayout:function(){L.Util.falseFn(e.body.offsetWidth)}}:{_animationStart:function(){},_animationZoomIn:function(t,e){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds())},_animationZoomOut:function(t,e){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds())},_animationAddLayer:function(t,e){this._animationAddLayerNonAnimated(t,e)}}),L.markerClusterGroup=function(t){return new L.MarkerClusterGroup(t)},L.MarkerCluster=L.Marker.extend({initialize:function(t,e,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this}),this._group=t,this._zoom=e,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(t){t=t||[];for(var e=this._childClusters.length-1;e>=0;e--)this._childClusters[e].getAllChildMarkers(t);for(var i=this._markers.length-1;i>=0;i--)t.push(this._markers[i]);return t},getChildCount:function(){return this._childCount},zoomToBounds:function(){this._group._map.fitBounds(this._bounds)},getBounds:function(){var t=new L.LatLngBounds;return t.extend(this._bounds),t},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(t,e){this._iconNeedsUpdate=!0,this._expandBounds(t),t instanceof L.MarkerCluster?(e||(this._childClusters.push(t),t.__parent=this),this._childCount+=t._childCount):(e||this._markers.push(t),this._childCount++),this.__parent&&this.__parent._addChild(t,!0)},_expandBounds:function(t){var e,i=t._wLatLng||t._latlng;t instanceof L.MarkerCluster?(this._bounds.extend(t._bounds),e=t._childCount):(this._bounds.extend(i),e=1),this._cLatLng||(this._cLatLng=t._cLatLng||i);var n=this._childCount+e;this._wLatLng?(this._wLatLng.lat=(i.lat*e+this._wLatLng.lat*this._childCount)/n,this._wLatLng.lng=(i.lng*e+this._wLatLng.lng*this._childCount)/n):this._latlng=this._wLatLng=new L.LatLng(i.lat,i.lng)},_addToMap:function(t){t&&(this._backupLatlng=this._latlng,this.setLatLng(t)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(t,e,i){this._recursively(t,0,i-1,function(t){var i,n,s=t._markers;for(i=s.length-1;i>=0;i--)n=s[i],n._icon&&(n._setPos(e),n.setOpacity(0))},function(t){var i,n,s=t._childClusters;for(i=s.length-1;i>=0;i--)n=s[i],n._icon&&(n._setPos(e),n.setOpacity(0))})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(t,e,i){this._recursively(t,i,0,function(n){n._recursivelyAnimateChildrenIn(t,n._group._map.latLngToLayerPoint(n.getLatLng()).round(),e),n._isSingleParent()&&e-1===i?(n.setOpacity(1),n._recursivelyRemoveChildrenFromMap(t,e)):n.setOpacity(0),n._addToMap()})},_recursivelyBecomeVisible:function(t,e){this._recursively(t,0,e,null,function(t){t.setOpacity(1)})},_recursivelyAddChildrenToMap:function(t,e,i){this._recursively(i,-1,e,function(n){if(e!==n._zoom)for(var s=n._markers.length-1;s>=0;s--){var r=n._markers[s];i.contains(r._latlng)&&(t&&(r._backupLatlng=r.getLatLng(),r.setLatLng(t),r.setOpacity&&r.setOpacity(0)),n._group._featureGroup.addLayer(r))}},function(e){e._addToMap(t)})},_recursivelyRestoreChildPositions:function(t){for(var e=this._markers.length-1;e>=0;e--){var i=this._markers[e];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(t-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var s=this._childClusters.length-1;s>=0;s--)this._childClusters[s]._recursivelyRestoreChildPositions(t)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(t,e,i){var n,s;this._recursively(t,-1,e-1,function(t){for(s=t._markers.length-1;s>=0;s--)n=t._markers[s],i&&i.contains(n._latlng)||(t._group._featureGroup.removeLayer(n),n.setOpacity&&n.setOpacity(1))},function(t){for(s=t._childClusters.length-1;s>=0;s--)n=t._childClusters[s],i&&i.contains(n._latlng)||(t._group._featureGroup.removeLayer(n),n.setOpacity&&n.setOpacity(1))})},_recursively:function(t,e,i,n,s){var r,o,a=this._childClusters,h=this._zoom;if(e>h)for(r=a.length-1;r>=0;r--)o=a[r],t.intersects(o._bounds)&&o._recursively(t,e,i,n,s);else if(n&&n(this),s&&this._zoom===i&&s(this),i>h)for(r=a.length-1;r>=0;r--)o=a[r],t.intersects(o._bounds)&&o._recursively(t,e,i,n,s)},_recalculateBounds:function(){var t,e=this._markers,i=this._childClusters;for(this._bounds=new L.LatLngBounds,delete this._wLatLng,t=e.length-1;t>=0;t--)this._expandBounds(e[t]);for(t=i.length-1;t>=0;t--)this._expandBounds(i[t])},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.DistanceGrid=function(t){this._cellSize=t,this._sqCellSize=t*t,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(t,e){var i=this._getCoord(e.x),n=this._getCoord(e.y),s=this._grid,r=s[n]=s[n]||{},o=r[i]=r[i]||[],a=L.Util.stamp(t);this._objectPoint[a]=e,o.push(t)},updateObject:function(t,e){this.removeObject(t),this.addObject(t,e)},removeObject:function(t,e){var i,n,s=this._getCoord(e.x),r=this._getCoord(e.y),o=this._grid,a=o[r]=o[r]||{},h=a[s]=a[s]||[];for(delete this._objectPoint[L.Util.stamp(t)],i=0,n=h.length;n>i;i++)if(h[i]===t)return h.splice(i,1),1===n&&delete a[s],!0},eachObject:function(t,e){var i,n,s,r,o,a,h,_=this._grid;for(i in _){o=_[i];for(n in o)for(a=o[n],s=0,r=a.length;r>s;s++)h=t.call(e,a[s]),h&&(s--,r--)}},getNearObject:function(t){var e,i,n,s,r,o,a,h,_=this._getCoord(t.x),u=this._getCoord(t.y),l=this._objectPoint,d=this._sqCellSize,p=null;for(e=u-1;u+1>=e;e++)if(s=this._grid[e])for(i=_-1;_+1>=i;i++)if(r=s[i])for(n=0,o=r.length;o>n;n++)a=r[n],h=this._sqDist(l[L.Util.stamp(a)],t),d>h&&(d=h,p=a);return p},_getCoord:function(t){return Math.floor(t/this._cellSize)},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(t,e){var i=e[1].lat-e[0].lat,n=e[0].lng-e[1].lng;return n*(t.lat-e[0].lat)+i*(t.lng-e[0].lng)},findMostDistantPointFromBaseLine:function(t,e){var i,n,s,r=0,o=null,a=[];for(i=e.length-1;i>=0;i--)n=e[i],s=this.getDistant(n,t),s>0&&(a.push(n),s>r&&(r=s,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(t,e){var i=[],n=this.findMostDistantPointFromBaseLine(t,e);return n.maxPoint?(i=i.concat(this.buildConvexHull([t[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,t[1]],n.newPoints))):[t]},getConvexHull:function(t){var e,i=!1,n=!1,s=null,r=null;for(e=t.length-1;e>=0;e--){var o=t[e];(i===!1||o.lat>i)&&(s=o,i=o.lat),(n===!1||o.lat=0;i--)e=n[i].getLatLng(),s.push(e);for(t=L.QuickHull.getConvexHull(s),i=t.length-1;i>=0;i--)r.push(t[i][0]);return r}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var t,e=this.getAllChildMarkers(),i=this._group,n=i._map,s=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,e.length>=this._circleSpiralSwitchover?t=this._generatePointsSpiral(e.length,s):(s.y+=10,t=this._generatePointsCircle(e.length,s)),this._animationSpiderfy(e,t)}},unspiderfy:function(t){this._group._inZoomAnimation||(this._animationUnspiderfy(t),this._group._spiderfied=null)},_generatePointsCircle:function(t,e){var i,n,s=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+t),r=s/this._2PI,o=this._2PI/t,a=[];for(a.length=t,i=t-1;i>=0;i--)n=this._circleStartAngle+i*o,a[i]=new L.Point(e.x+r*Math.cos(n),e.y+r*Math.sin(n))._round();return a},_generatePointsSpiral:function(t,e){var i,n=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthStart,s=this._group.options.spiderfyDistanceMultiplier*this._spiralFootSeparation,r=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthFactor,o=0,a=[];for(a.length=t,i=t-1;i>=0;i--)o+=s/n+5e-4*i,a[i]=new L.Point(e.x+n*Math.cos(o),e.y+n*Math.sin(o))._round(),n+=this._2PI*r/o;return a},_noanimationUnspiderfy:function(){var t,e,i=this._group,n=i._map,s=i._featureGroup,r=this.getAllChildMarkers();for(this.setOpacity(1),e=r.length-1;e>=0;e--)t=r[e],s.removeLayer(t),t._preSpiderfyLatlng&&(t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng),t.setZIndexOffset&&t.setZIndexOffset(0),t._spiderLeg&&(n.removeLayer(t._spiderLeg),delete t._spiderLeg)}}),L.MarkerCluster.include(L.DomUtil.TRANSITION?{SVG_ANIMATION:function(){return e.createElementNS("http://www.w3.org/2000/svg","animate").toString().indexOf("SVGAnimate")>-1}(),_animationSpiderfy:function(t,i){var n,s,r,o,a=this,h=this._group,_=h._map,u=h._featureGroup,l=_.latLngToLayerPoint(this._latlng);for(n=t.length-1;n>=0;n--)s=t[n],s.setOpacity?(s.setZIndexOffset(1e6),s.setOpacity(0),u.addLayer(s),s._setPos(l)):u.addLayer(s);h._forceLayout(),h._animationStart();var d=L.Path.SVG?0:.3,p=L.Path.SVG_NS;for(n=t.length-1;n>=0;n--)if(o=_.layerPointToLatLng(i[n]),s=t[n],s._preSpiderfyLatlng=s._latlng,s.setLatLng(o),s.setOpacity&&s.setOpacity(1),r=new L.Polyline([a._latlng,o],{weight:1.5,color:"#222",opacity:d}),_.addLayer(r),s._spiderLeg=r,L.Path.SVG&&this.SVG_ANIMATION){var c=r._path.getTotalLength();r._path.setAttribute("stroke-dasharray",c+","+c);var m=e.createElementNS(p,"animate");m.setAttribute("attributeName","stroke-dashoffset"),m.setAttribute("begin","indefinite"),m.setAttribute("from",c),m.setAttribute("to",0),m.setAttribute("dur",.25),r._path.appendChild(m),m.beginElement(),m=e.createElementNS(p,"animate"),m.setAttribute("attributeName","stroke-opacity"),m.setAttribute("attributeName","stroke-opacity"),m.setAttribute("begin","indefinite"),m.setAttribute("from",0),m.setAttribute("to",.5),m.setAttribute("dur",.25),r._path.appendChild(m),m.beginElement()}if(a.setOpacity(.3),L.Path.SVG)for(this._group._forceLayout(),n=t.length-1;n>=0;n--)s=t[n]._spiderLeg,s.options.opacity=.5,s._path.setAttribute("stroke-opacity",.5);setTimeout(function(){h._animationEnd(),h.fire("spiderfied")},200)},_animationUnspiderfy:function(t){var e,i,n,s=this._group,r=s._map,o=s._featureGroup,a=t?r._latLngToNewLayerPoint(this._latlng,t.zoom,t.center):r.latLngToLayerPoint(this._latlng),h=this.getAllChildMarkers(),_=L.Path.SVG&&this.SVG_ANIMATION;for(s._animationStart(),this.setOpacity(1),i=h.length-1;i>=0;i--)e=h[i],e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng,e.setOpacity?(e._setPos(a),e.setOpacity(0)):o.removeLayer(e),_&&(n=e._spiderLeg._path.childNodes[0],n.setAttribute("to",n.getAttribute("from")),n.setAttribute("from",0),n.beginElement(),n=e._spiderLeg._path.childNodes[1],n.setAttribute("from",.5),n.setAttribute("to",0),n.setAttribute("stroke-opacity",0),n.beginElement(),e._spiderLeg._path.setAttribute("stroke-opacity",0)));setTimeout(function(){var t=0;for(i=h.length-1;i>=0;i--)e=h[i],e._spiderLeg&&t++;for(i=h.length-1;i>=0;i--)e=h[i],e._spiderLeg&&(e.setOpacity&&(e.setOpacity(1),e.setZIndexOffset(0)),t>1&&o.removeLayer(e),r.removeLayer(e._spiderLeg),delete e._spiderLeg);s._animationEnd()},200)}}:{_animationSpiderfy:function(t,e){var i,n,s,r,o=this._group,a=o._map,h=o._featureGroup;for(i=t.length-1;i>=0;i--)r=a.layerPointToLatLng(e[i]),n=t[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(r),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n),s=new L.Polyline([this._latlng,r],{weight:1.5,color:"#222"}),a.addLayer(s),n._spiderLeg=s;this.setOpacity(.3),o.fire("spiderfied")},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerClusterGroup.include({_spiderfied:null,_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation?this._map.on("zoomstart",this._unspiderfyZoomStart,this):this._map.on("zoomend",this._unspiderfyWrapper,this),L.Path.SVG&&!L.Browser.touch&&this._map._initPathRoot()},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy()},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(t){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(t))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(t){this._spiderfied&&this._spiderfied.unspiderfy(t)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(t){t._spiderLeg&&(this._featureGroup.removeLayer(t),t.setOpacity(1),t.setZIndexOffset(0),this._map.removeLayer(t._spiderLeg),delete t._spiderLeg)}})}(window,document); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.PolylineDecorator-master/LICENSE ================================================ MIT License Copyright (c) 2013 Benjamin Becquet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.PolylineDecorator-master/README.md ================================================ # Leaflet PolylineDecorator A Leaflet plug-in to define and draw patterns on existing Polylines or along coordinate paths. ## Features * Dashed or dotted lines, arrow heads, markers following line * Works on Polygons too! (easy, as Polygon extends Polyline) * Multiple patterns can be applied to the same line * New behaviors can be obtained by defining new symbols ## Usage ```javascript var polyline = L.polyline([...]).addTo(map); var decorator = L.polylineDecorator(polyline, { patterns: [ // define a pattern of 10px-wide dashes, repeated every 20px on the line {offset: 0, repeat: '20px', symbol: new L.Symbol.Dash({pixelSize: 10})} ] }).addTo(map); ``` The `polyline` parameter can be a single array of `L.LatLng` or, with Leaflet's simplified syntax, an array of 2-cells arrays of coordinates. It is useful if you don't want to actually display a polyline, but just a pattern following coordinates, like a dotted line. ## Screenshot ![screenshot](https://raw.github.com/bbecquet/Leaflet.PolylineDecorator/master/screenshot.png "Screenshot showing different applications of the library") ## Performance note Please note that this library is in an early stage, and many operations could still be optimized. Moreover, as it requires a lot of (re-)computations, and each pattern symbol is an actual `L.ILayer` object, it can have an impact on the responsiveness of your map, especially if used on many objects. In cases where it's applicable (dash patterns), you should probably use instead the `dashArray` property of `L.Path`, as it's natively drawn by the browser. ## TODO * Documentation * Optimize rendering and mem footprint * Other symbol types * Animations(?) ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.PolylineDecorator-master/example/example.html ================================================  Leaflet Polyline Decorator example
                            ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.PolylineDecorator-master/example/example.js ================================================  function init() { var map = new L.Map('map', { center: [52.0, -11.0], zoom: 5, layers: [ new L.TileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', { attribution: 'Tiles Courtesy of MapQuest — Map data © OpenStreetMap contributors', maxZoom: 18, subdomains: '1234' }) ] }); // --- Arrow, with animation to demonstrate the use of setPatterns --- var arrow = L.polyline([[57, -19], [60, -12]], {}).addTo(map); var arrowHead = L.polylineDecorator(arrow).addTo(map); var arrowOffset = 0; var anim = window.setInterval(function() { arrowHead.setPatterns([ {offset: arrowOffset+'%', repeat: 0, symbol: L.Symbol.arrowHead({pixelSize: 15, polygon: false, pathOptions: {stroke: true}})} ]) if(++arrowOffset > 100) arrowOffset = 0; }, 100); // --- Polygon --- var polygon = L.polygon([[54, -6], [55, -7], [56, -2], [55, 1], [53, 0], [54, -6]], {color: "#ff7800", weight: 1}).addTo(map); var pd = L.polylineDecorator(polygon, { patterns: [ {offset: 0, repeat: 10, symbol: L.Symbol.dash({pixelSize: 0})} ] }).addTo(map); // --- Multi-pattern without Polyline --- var pathPattern = L.polylineDecorator( [ [ 49.543519, -12.469833 ], [ 49.808981, -12.895285 ], [ 50.056511, -13.555761 ], [ 50.217431, -14.758789 ], [ 50.476537, -15.226512 ], [ 50.377111, -15.706069 ], [ 50.200275, -16.000263 ], [ 49.860606, -15.414253 ], [ 49.672607, -15.710152 ], [ 49.863344, -16.451037 ], [ 49.774564, -16.875042 ], [ 49.498612, -17.106036 ], [ 49.435619, -17.953064 ], [ 49.041792, -19.118781 ], [ 48.548541, -20.496888 ], [ 47.930749, -22.391501 ], [ 47.547723, -23.781959 ], [ 47.095761, -24.941630 ], [ 46.282478, -25.178463 ], [ 45.409508, -25.601434 ], [ 44.833574, -25.346101 ], [ 44.039720, -24.988345 ] ], { patterns: [ { offset: 12, repeat: 25, symbol: L.Symbol.dash({pixelSize: 10, pathOptions: {color: '#f00', weight: 2}}) }, { offset: 0, repeat: 25, symbol: L.Symbol.dash({pixelSize: 0}) } ] } ).addTo(map); // --- Markers proportionnaly located --- var markerLine = L.polyline([[58.44773, -28.65234], [52.9354, -23.33496], [53.01478, -14.32617], [58.1707, -10.37109], [59.68993, -0.65918]], {}).addTo(map); var markerPatterns = L.polylineDecorator(markerLine, { patterns: [ { offset: '5%', repeat: '10%', symbol: L.Symbol.marker()} ] }).addTo(map); // --- Example with a rotated marker --- var pathPattern = L.polylineDecorator( [ [ 42.9, -15 ], [ 44.18, -11.4 ], [ 45.77, -8.0 ], [ 47.61, -6.4 ], [ 49.41, -6.1 ], [ 51.01, -7.2 ] ], { patterns: [ { offset: 0, repeat: 10, symbol: L.Symbol.dash({pixelSize: 5, pathOptions: {color: '#000', weight: 1, opacity: 0.2}}) }, { offset: '16%', repeat: '33%', symbol: L.Symbol.marker({rotate: true, markerOptions: { icon: L.icon({ iconUrl: 'icon_plane.png', iconAnchor: [16, 16] }) }})} ] } ).addTo(map); // --- Example with an array of Polylines --- var multiCoords1 = [ [[47.5468, -0.7910], [48.8068, -0.1318], [49.1242, 1.6699], [49.4966, 3.2958], [51.4266, 2.8564], [51.7542, 2.1093]], [[48.0193, -2.8125], [46.3165, -2.8564], [44.9336, -1.0107], [44.5278, 1.5820], [44.8714, 3.7353], [45.8287, 5.1855], [48.1953, 5.1416]], [[45.9205, 0.4394], [46.7699, 0.9228], [47.6061, 2.5488], [47.7540, 3.3837]] ]; var plArray = []; for(var i=0; i 0) { // 3. consider only the rest of the path, starting at the previous point var remainingPath = pts; remainingPath = remainingPath.slice(previous.predecessor); remainingPath[0] = previous.pt; var remainingLength = this.getPointPathPixelLength(remainingPath); // 4. project as a ratio of the remaining length, // and repeat while there is room for another point of the pattern while(repeatIntervalLength <= remainingLength) { previous = this.interpolateOnPointPath(remainingPath, repeatIntervalLength/remainingLength); positions.push(previous); remainingPath = remainingPath.slice(previous.predecessor); remainingPath[0] = previous.pt; remainingLength = this.getPointPathPixelLength(remainingPath); } } return positions; }, /** * pts: array of L.Point * ratio: the ratio of the total length where the point should be computed * Returns null if ll has less than 2 LatLng, or an object with the following properties: * latLng: the LatLng of the interpolated point * predecessor: the index of the previous vertex on the path * heading: the heading of the path at this point, in degrees */ interpolateOnPointPath: function (pts, ratio) { var nbVertices = pts.length; if (nbVertices < 2) { return null; } // easy limit cases: ratio negative/zero => first vertex if (ratio <= 0) { return { pt: pts[0], predecessor: 0, heading: this.computeAngle(pts[0], pts[1]) }; } // ratio >=1 => last vertex if (ratio >= 1) { return { pt: pts[nbVertices - 1], predecessor: nbVertices - 1, heading: this.computeAngle(pts[nbVertices - 2], pts[nbVertices - 1]) }; } // 1-segment-only path => direct linear interpolation if (nbVertices == 2) { return { pt: this.interpolateBetweenPoints(pts[0], pts[1], ratio), predecessor: 0, heading: this.computeAngle(pts[0], pts[1]) }; } var pathLength = this.getPointPathPixelLength(pts); var a = pts[0], b = a, ratioA = 0, ratioB = 0, distB = 0; // follow the path segments until we find the one // on which the point must lie => [ab] var i = 1; for (; i < nbVertices && ratioB < ratio; i++) { a = b; ratioA = ratioB; b = pts[i]; distB += a.distanceTo(b); ratioB = distB / pathLength; } // compute the ratio relative to the segment [ab] var segmentRatio = (ratio - ratioA) / (ratioB - ratioA); return { pt: this.interpolateBetweenPoints(a, b, segmentRatio), predecessor: i-2, heading: this.computeAngle(a, b) }; }, /** * Finds the point which lies on the segment defined by points A and B, * at the given ratio of the distance from A to B, by linear interpolation. */ interpolateBetweenPoints: function (ptA, ptB, ratio) { if(ptB.x != ptA.x) { return new L.Point( (ptA.x * (1 - ratio)) + (ratio * ptB.x), (ptA.y * (1 - ratio)) + (ratio * ptB.y) ); } // special case where points lie on the same vertical axis return new L.Point(ptA.x, ptA.y + (ptB.y - ptA.y) * ratio); } }; L.RotatedMarker = L.Marker.extend({ options: { angle: 0 }, _setPos: function (pos) { L.Marker.prototype._setPos.call(this, pos); if (L.DomUtil.TRANSFORM) { // use the CSS transform rule if available this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)'; } else if(L.Browser.ie) { // fallback for IE6, IE7, IE8 var rad = this.options.angle * L.LatLng.DEG_TO_RAD, costheta = Math.cos(rad), sintheta = Math.sin(rad); this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' + costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')'; } } }); L.rotatedMarker = function (pos, options) { return new L.RotatedMarker(pos, options); };/** * Defines several classes of symbol factories, * to be used with L.PolylineDecorator */ L.Symbol = L.Symbol || {}; /** * A simple dash symbol, drawn as a Polyline. * Can also be used for dots, if 'pixelSize' option is given the 0 value. */ L.Symbol.Dash = L.Class.extend({ isZoomDependant: true, options: { pixelSize: 10, pathOptions: { } }, initialize: function (options) { L.Util.setOptions(this, options); this.options.pathOptions.clickable = false; }, buildSymbol: function(dirPoint, latLngs, map, index, total) { var opts = this.options; // for a dot, nothing more to compute if(opts.pixelSize <= 1) { return new L.Polyline([dirPoint.latLng, dirPoint.latLng], opts.pathOptions); } var midPoint = map.project(dirPoint.latLng); var angle = (-(dirPoint.heading - 90)) * L.LatLng.DEG_TO_RAD; var a = new L.Point( midPoint.x + opts.pixelSize * Math.cos(angle + Math.PI) / 2, midPoint.y + opts.pixelSize * Math.sin(angle) / 2 ); // compute second point by central symmetry to avoid unecessary cos/sin var b = midPoint.add(midPoint.subtract(a)); return new L.Polyline([map.unproject(a), map.unproject(b)], opts.pathOptions); } }); L.Symbol.dash = function (options) { return new L.Symbol.Dash(options); }; L.Symbol.ArrowHead = L.Class.extend({ isZoomDependant: true, options: { polygon: true, pixelSize: 10, headAngle: 60, pathOptions: { stroke: false, weight: 2 } }, initialize: function (options) { L.Util.setOptions(this, options); this.options.pathOptions.clickable = false; }, buildSymbol: function(dirPoint, latLngs, map, index, total) { var opts = this.options; var path; if(opts.polygon) { path = new L.Polygon(this._buildArrowPath(dirPoint, map), opts.pathOptions); } else { path = new L.Polyline(this._buildArrowPath(dirPoint, map), opts.pathOptions); } return path; }, _buildArrowPath: function (dirPoint, map) { var tipPoint = map.project(dirPoint.latLng); var direction = (-(dirPoint.heading - 90)) * L.LatLng.DEG_TO_RAD; var radianArrowAngle = this.options.headAngle / 2 * L.LatLng.DEG_TO_RAD; var headAngle1 = direction + radianArrowAngle, headAngle2 = direction - radianArrowAngle; var arrowHead1 = new L.Point( tipPoint.x - this.options.pixelSize * Math.cos(headAngle1), tipPoint.y + this.options.pixelSize * Math.sin(headAngle1)), arrowHead2 = new L.Point( tipPoint.x - this.options.pixelSize * Math.cos(headAngle2), tipPoint.y + this.options.pixelSize * Math.sin(headAngle2)); return [ map.unproject(arrowHead1), dirPoint.latLng, map.unproject(arrowHead2) ]; } }); L.Symbol.arrowHead = function (options) { return new L.Symbol.ArrowHead(options); }; L.Symbol.Marker = L.Class.extend({ isZoomDependant: false, options: { markerOptions: { }, rotate: false }, initialize: function (options) { L.Util.setOptions(this, options); this.options.markerOptions.clickable = false; this.options.markerOptions.draggable = false; this.isZoomDependant = (L.Browser.ie && this.options.rotate); }, buildSymbol: function(directionPoint, latLngs, map, index, total) { if(!this.options.rotate) { return new L.Marker(directionPoint.latLng, this.options.markerOptions); } else { this.options.markerOptions.angle = directionPoint.heading; return new L.RotatedMarker(directionPoint.latLng, this.options.markerOptions); } } }); L.Symbol.marker = function (options) { return new L.Symbol.Marker(options); }; L.PolylineDecorator = L.LayerGroup.extend({ options: { patterns: [] }, initialize: function(paths, options) { L.LayerGroup.prototype.initialize.call(this); L.Util.setOptions(this, options); this._map = null; this._initPaths(paths); this._initPatterns(); }, /** * Deals with all the different cases. p can be one of these types: * array of LatLng, array of 2-number arrays, Polyline, Polygon, * array of one of the previous, MultiPolyline, MultiPolygon. */ _initPaths: function(p) { this._paths = []; var isPolygon = false; if(p instanceof L.MultiPolyline || (isPolygon = (p instanceof L.MultiPolygon))) { var lines = p.getLatLngs(); for(var i=0; i 0) { if(p[0] instanceof L.Polyline) { for(var i=0; i 0 && ( ll[0] instanceof L.LatLng || (L.Util.isArray(ll[0]) && ll[0].length == 2 && typeof ll[0][0] === 'number') )); }, _initPath: function(path, isPolygon) { var latLngs; // It may still be an array of array of coordinates // (ex: polygon with rings) if(this._isCoordArray(path)) { latLngs = [path]; } else { latLngs = path; } for(var i=0; i= v0.6, last polygon vertex (=first) isn't repeated. // Our algorithm needs it, so we add it back explicitly. if(isPolygon) { latLngs[i].push(latLngs[i][0]); } this._paths.push(latLngs[i]); } }, _initPatterns: function() { this._isZoomDependant = false; this._patterns = []; var pattern; // parse pattern definitions and precompute some values for(var i=0;i 0); } if(typeof patternDef.repeat === 'string' && patternDef.repeat.indexOf('%') != -1) { pattern.repeat = parseFloat(patternDef.repeat) / 100; } else { pattern.repeat = parseFloat(patternDef.repeat); pattern.isRepeatInPixels = (pattern.repeat > 0); } // TODO: 0 => not pixel dependant => 0% return(pattern); }, onAdd: function (map) { this._map = map; this._draw(); // listen to zoom changes to redraw pixel-spaced patterns if(this._isZoomDependant) { this._map.on('zoomend', this._softRedraw, this); } }, onRemove: function (map) { // remove optional map zoom listener this._map.off('zoomend', this._softRedraw, this); this._map = null; L.LayerGroup.prototype.onRemove.call(this, map); }, /** * Returns an array of ILayers object */ _buildSymbols: function(latLngs, symbolFactory, directionPoints) { var symbols = []; for(var i=0, l=directionPoints.length; i 0) { // 3. consider only the rest of the path, starting at the previous point var remainingPath = pts; remainingPath = remainingPath.slice(previous.predecessor); remainingPath[0] = previous.pt; var remainingLength = this.getPointPathPixelLength(remainingPath); // 4. project as a ratio of the remaining length, // and repeat while there is room for another point of the pattern while(repeatIntervalLength <= remainingLength) { previous = this.interpolateOnPointPath(remainingPath, repeatIntervalLength/remainingLength); positions.push(previous); remainingPath = remainingPath.slice(previous.predecessor); remainingPath[0] = previous.pt; remainingLength = this.getPointPathPixelLength(remainingPath); } } return positions; }, /** * pts: array of L.Point * ratio: the ratio of the total length where the point should be computed * Returns null if ll has less than 2 LatLng, or an object with the following properties: * latLng: the LatLng of the interpolated point * predecessor: the index of the previous vertex on the path * heading: the heading of the path at this point, in degrees */ interpolateOnPointPath: function (pts, ratio) { var nbVertices = pts.length; if (nbVertices < 2) { return null; } // easy limit cases: ratio negative/zero => first vertex if (ratio <= 0) { return { pt: pts[0], predecessor: 0, heading: this.computeAngle(pts[0], pts[1]) }; } // ratio >=1 => last vertex if (ratio >= 1) { return { pt: pts[nbVertices - 1], predecessor: nbVertices - 1, heading: this.computeAngle(pts[nbVertices - 2], pts[nbVertices - 1]) }; } // 1-segment-only path => direct linear interpolation if (nbVertices == 2) { return { pt: this.interpolateBetweenPoints(pts[0], pts[1], ratio), predecessor: 0, heading: this.computeAngle(pts[0], pts[1]) }; } var pathLength = this.getPointPathPixelLength(pts); var a = pts[0], b = a, ratioA = 0, ratioB = 0, distB = 0; // follow the path segments until we find the one // on which the point must lie => [ab] var i = 1; for (; i < nbVertices && ratioB < ratio; i++) { a = b; ratioA = ratioB; b = pts[i]; distB += a.distanceTo(b); ratioB = distB / pathLength; } // compute the ratio relative to the segment [ab] var segmentRatio = (ratio - ratioA) / (ratioB - ratioA); return { pt: this.interpolateBetweenPoints(a, b, segmentRatio), predecessor: i-2, heading: this.computeAngle(a, b) }; }, /** * Finds the point which lies on the segment defined by points A and B, * at the given ratio of the distance from A to B, by linear interpolation. */ interpolateBetweenPoints: function (ptA, ptB, ratio) { if(ptB.x != ptA.x) { return new L.Point( (ptA.x * (1 - ratio)) + (ratio * ptB.x), (ptA.y * (1 - ratio)) + (ratio * ptB.y) ); } // special case where points lie on the same vertical axis return new L.Point(ptA.x, ptA.y + (ptB.y - ptA.y) * ratio); } }; ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.PolylineDecorator-master/src/L.PolylineDecorator.js ================================================ L.PolylineDecorator = L.LayerGroup.extend({ options: { patterns: [] }, initialize: function(paths, options) { L.LayerGroup.prototype.initialize.call(this); L.Util.setOptions(this, options); this._map = null; this._initPaths(paths); this._initPatterns(); }, /** * Deals with all the different cases. p can be one of these types: * array of LatLng, array of 2-number arrays, Polyline, Polygon, * array of one of the previous, MultiPolyline, MultiPolygon. */ _initPaths: function(p) { this._paths = []; var isPolygon = false; if(p instanceof L.MultiPolyline || (isPolygon = (p instanceof L.MultiPolygon))) { var lines = p.getLatLngs(); for(var i=0; i 0) { if(p[0] instanceof L.Polyline) { for(var i=0; i 0 && ( ll[0] instanceof L.LatLng || (L.Util.isArray(ll[0]) && ll[0].length == 2 && typeof ll[0][0] === 'number') )); }, _initPath: function(path, isPolygon) { var latLngs; // It may still be an array of array of coordinates // (ex: polygon with rings) if(this._isCoordArray(path)) { latLngs = [path]; } else { latLngs = path; } for(var i=0; i= v0.6, last polygon vertex (=first) isn't repeated. // Our algorithm needs it, so we add it back explicitly. if(isPolygon) { latLngs[i].push(latLngs[i][0]); } this._paths.push(latLngs[i]); } }, _initPatterns: function() { this._isZoomDependant = false; this._patterns = []; var pattern; // parse pattern definitions and precompute some values for(var i=0;i 0); } if(typeof patternDef.repeat === 'string' && patternDef.repeat.indexOf('%') != -1) { pattern.repeat = parseFloat(patternDef.repeat) / 100; } else { pattern.repeat = parseFloat(patternDef.repeat); pattern.isRepeatInPixels = (pattern.repeat > 0); } // TODO: 0 => not pixel dependant => 0% return(pattern); }, onAdd: function (map) { this._map = map; this._draw(); // listen to zoom changes to redraw pixel-spaced patterns if(this._isZoomDependant) { this._map.on('zoomend', this._softRedraw, this); } }, onRemove: function (map) { // remove optional map zoom listener this._map.off('zoomend', this._softRedraw, this); this._map = null; L.LayerGroup.prototype.onRemove.call(this, map); }, /** * Returns an array of ILayers object */ _buildSymbols: function(latLngs, symbolFactory, directionPoints) { var symbols = []; for(var i=0, l=directionPoints.length; i错误: 形状边缘不能交叉!', tooltip : { start : '点击开始画线', cont : '继续点击画线', end : '点击最后一个点结束画线' } }, rectangle : { tooltip : { start : '点击画矩形' } }, reset : { tooltip : { start : '重置' } }, simpleshape : { tooltip : { end : '松开鼠标结束绘制' } } } }, edit : { toolbar : { actions : { save : { title : '保存修改.', text : '保存' }, cancel : { title : '取消编辑,丢弃所有的变化。', text : '取消' } }, buttons : { edit : '编辑图层', editDisabled : '没有图层可编辑', remove : '删除图层', removeDisabled : '没有图层删除' } }, handlers : { edit : { tooltip : { text : '拖动把手,或标记编辑功能。', subtext : '单击取消撤销更改。' } }, remove : { tooltip : { text : '点击取消' } } } } }; L.Draw = {}; L.Draw.Feature = L.Handler.extend({ includes : L.Mixin.Events, initialize : function(map, options) { this._map = map; this._container = map._container; this._overlayPane = map._panes.overlayPane; this._popupPane = map._panes.popupPane; // Merge default shapeOptions options with custom shapeOptions if (options && options.shapeOptions) { options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions); } L.setOptions(this, options); }, enable : function() { if (this._enabled) { return; } L.Handler.prototype.enable.call(this); this.fire('enabled', { handler : this.type }); this._map.fire('draw:drawstart', { layerType : this.type }); }, disable : function() { if (!this._enabled) { return; } L.Handler.prototype.disable.call(this); this.fire('disabled', { handler : this.type }); this._map.fire('draw:drawstop', { layerType : this.type }); }, addHooks : function() { var map = this._map; if (map) { L.DomUtil.disableTextSelection(); map.getContainer().focus(); this._tooltip = new L.Tooltip(this._map); L.DomEvent.addListener(this._container, 'keyup', this._cancelDrawing, this); } }, removeHooks : function() { if (this._map) { L.DomUtil.enableTextSelection(); this._tooltip.dispose(); this._tooltip = null; L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing); } }, setOptions : function(options) { L.setOptions(this, options); }, _fireCreatedEvent : function(layer) { this._map.fire('draw:created', { layer : layer, layerType : this.type }); }, // Cancel drawing when the escape key is pressed _cancelDrawing : function(e) { if (e.keyCode === 27) { this.disable(); } } }); L.Draw.Polyline = L.Draw.Feature .extend({ statics : { TYPE : 'polyline' }, Poly : L.Polyline, options : { allowIntersection : true, repeatMode : false, drawError : { color : '#b00b00', timeout : 2500 }, icon : new L.DivIcon({ iconSize : new L.Point(8, 8), className : 'leaflet-div-icon leaflet-editing-icon' }), guidelineDistance : 20, shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : false, clickable : true }, metric : true, // Whether to use the metric meaurement // system or imperial showLength : true, // Whether to display distance in the // tooltip zIndexOffset : 2000 // This should be > than the highest z-index any map layers }, initialize : function(map, options) { this._map = map; // Need to set this here to ensure the correct message is // used. this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error; // Merge default drawError options with custom options if (options && options.drawError) { options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); } // Save the type so super can fire, need to do this as // cannot do this.TYPE :( this.type = L.Draw.Polyline.TYPE; // 自定义的 this._drawnItems = new L.FeatureGroup(); this._map.addLayer(this._drawnItems); L.Draw.Feature.prototype.initialize .call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { this._markers = []; // 初始化绘制完成状态为false; this._drawStatus = false; // 扩展的属性 this._tipMarkerGroup = new L.LayerGroup(); this._map.addLayer(this._tipMarkerGroup); this._tipIdPrefix = 'tip_' + (new Date()).getTime(); // /// this._markerGroup = new L.LayerGroup(); this._map.addLayer(this._markerGroup); this._poly = new L.Polyline([], this.options.shapeOptions); this._tooltip.updateContent(this._getTooltipText()); // Make a transparent marker that will used to catch // click events. These click // events will create the vertices. We need to do this // so we can ensure that // we can create vertices over other map layers // (markers, vector layers). We // also do not want to trigger any click handlers of // objects we are clicking on // while drawing. if (!this._mouseMarker) { this._mouseMarker = L .marker( this._map.getCenter(), { icon : L .divIcon({ className : 'leaflet-mouse-marker', iconAnchor : [ 20, 20 ], iconSize : [ 40, 40 ] }), opacity : 0, zIndexOffset : this.options.zIndexOffset }); } this._mouseMarker.on('click', this._onClick, this) .addTo(this._map); this._map.on('mousemove', this._onMouseMove, this).on( 'zoomend', this._onZoomEnd, this); } }, removeHooks : function() { L.Draw.Feature.prototype.removeHooks.call(this); this._clearHideErrorTimeout(); this._cleanUpShape(); // remove markers from map this._map.removeLayer(this._markerGroup); delete this._markerGroup; delete this._markers; this._map.removeLayer(this._poly); delete this._poly; this._mouseMarker.off('click', this._onClick, this); this._map.removeLayer(this._mouseMarker); delete this._mouseMarker; // clean up DOM this._clearGuides(); this._map.off('mousemove', this._onMouseMove, this).off( 'zoomend', this._onZoomEnd, this); }, _finishShape : function() { var intersects = this._poly.newLatLngIntersects(this._poly .getLatLngs()[0], true); if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { this._showErrorTooltip(); return; } this._fireCreatedEvent(); this.disable(); if (this.options.repeatMode) { this.enable(); } }, // Called to verify the shape is valid when the user tries to // finish it // Return false if the shape is not valid _shapeIsValid : function() { return true; }, _onZoomEnd : function() { this._updateGuide(); }, _onMouseMove : function(e) { var newPos = e.layerPoint, latlng = e.latlng; // Save latlng // should this be moved to _updateGuide() ? this._currentLatLng = latlng; this._updateTooltip(latlng); // Update the guide line this._updateGuide(newPos); // Update the mouse marker position this._mouseMarker.setLatLng(latlng); L.DomEvent.preventDefault(e.originalEvent); }, _onClick : function(e) { var latlng = e.target.getLatLng(), markerCount = this._markers.length; if (markerCount > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { this._showErrorTooltip(); return; } else if (this._errorShown) { this._hideErrorTooltip(); } this._markers.push(this._createMarker(latlng)); this._poly.addLatLng(latlng); if (this._poly.getLatLngs().length === 2) { this._map.addLayer(this._poly); } this._updateFinishHandler(); this._vertexAdded(latlng); this._clearGuides(); this._updateTooltip(); // 测距添加Tip if (this.type == 'polyline') { var tip = this._getTooltipText().subtext; if (this._markers.length == 1) { tip = '起点'; } var myIcon = L .divIcon({ iconSize : L.point(90, 20), iconAnchor : L.point(50, 25), html : '
                            ' + tip + '
                            ' }); var tipMarker = L.marker(latlng, { icon : myIcon }).addTo(this._map); this._tipMarkerGroup.addLayer(tipMarker); } }, _updateFinishHandler : function() { var markerCount = this._markers.length; // The last marker should have a click handler to close the // polyline if (markerCount > 1) { this._markers[markerCount - 1].on('click', this._finishShape, this); } // Remove the old marker click handler (as only the last // point should close the polyline) if (markerCount > 2) { this._markers[markerCount - 2].off('click', this._finishShape, this); } }, _createMarker : function(latlng) { var marker = new L.Marker(latlng, { icon : this.options.icon, zIndexOffset : this.options.zIndexOffset * 2 }); this._markerGroup.addLayer(marker); return marker; }, _updateGuide : function(newPos) { var markerCount = this._markers.length; if (markerCount > 0) { newPos = newPos || this._map .latLngToLayerPoint(this._currentLatLng); // draw the guide line this._clearGuides(); this ._drawGuide( this._map .latLngToLayerPoint(this._markers[markerCount - 1] .getLatLng()), newPos); } }, _updateTooltip : function(latLng) { var text = this._getTooltipText(); if (latLng) { this._tooltip.updatePosition(latLng); } if (!this._errorShown) { this._tooltip.updateContent(text); } }, _drawGuide : function(pointA, pointB) { var length = Math.floor(Math.sqrt(Math.pow( (pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))), i, fraction, dashPoint, dash; // create the guides container if we haven't yet if (!this._guidesContainer) { this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane); } // draw a dash every GuildeLineDistance for (i = this.options.guidelineDistance; i < length; i += this.options.guidelineDistance) { // work out fraction along line we are fraction = i / length; // calculate new x,y point dashPoint = { x : Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)), y : Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y)) }; // add guide dash to guide container dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer); dash.style.backgroundColor = !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color; L.DomUtil.setPosition(dash, dashPoint); } }, _updateGuideColor : function(color) { if (this._guidesContainer) { for ( var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) { this._guidesContainer.childNodes[i].style.backgroundColor = color; } } }, // removes all child elements (guide dashes) from the guides // container _clearGuides : function() { if (this._guidesContainer) { while (this._guidesContainer.firstChild) { this._guidesContainer .removeChild(this._guidesContainer.firstChild); } } }, _getTooltipText : function() { var showLength = this.options.showLength, labelText, distance, distanceStr; if (this._markers.length === 0) { labelText = { text : L.drawLocal.draw.handlers.polyline.tooltip.start }; } else { distanceStr = showLength ? this._getMeasurementString() : ''; if (this._markers.length === 1) { labelText = { text : L.drawLocal.draw.handlers.polyline.tooltip.cont, subtext : distanceStr }; } else { labelText = { text : L.drawLocal.draw.handlers.polyline.tooltip.end, subtext : distanceStr }; } } return labelText; }, _getMeasurementString : function() { var currentLatLng = this._currentLatLng, previousLatLng = this._markers[this._markers.length - 1] .getLatLng(), distance; // calculate the distance from the last fixed point to the // mouse position distance = this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng); return L.GeometryUtil.readableDistance(distance, this.options.metric); }, _showErrorTooltip : function() { this._errorShown = true; // Update tooltip this._tooltip.showAsError().updateContent({ text : this.options.drawError.message }); // Update shape this._updateGuideColor(this.options.drawError.color); this._poly.setStyle({ color : this.options.drawError.color }); // Hide the error after 2 seconds this._clearHideErrorTimeout(); this._hideErrorTimeout = setTimeout(L.Util.bind( this._hideErrorTooltip, this), this.options.drawError.timeout); }, _hideErrorTooltip : function() { this._errorShown = false; this._clearHideErrorTimeout(); // Revert tooltip this._tooltip.removeError().updateContent( this._getTooltipText()); // Revert shape this._updateGuideColor(this.options.shapeOptions.color); this._poly.setStyle({ color : this.options.shapeOptions.color }); }, _clearHideErrorTimeout : function() { if (this._hideErrorTimeout) { clearTimeout(this._hideErrorTimeout); this._hideErrorTimeout = null; } }, _vertexAdded : function(latlng) { if (this._markers.length === 1) { this._measurementRunningTotal = 0; } else { this._measurementRunningTotal += latlng .distanceTo(this._markers[this._markers.length - 2] .getLatLng()); } }, _cleanUpShape : function() { if (this._markers.length > 1) { this._markers[this._markers.length - 1].off('click', this._finishShape, this); // 如果不是正常绘制完成的话,删除已经添加的tipMarker if (!this._drawStatus) { this._map.removeLayer(this._tipMarkerGroup); } } }, _fireCreatedEvent : function() { this._drawStatus = true; var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly); this._drawnItems.addLayer(poly); L.Draw.tempData = { tipMarkerGroup : this._tipMarkerGroup, layer : this._drawnItems } } }); L.Draw.Polygon = L.Draw.Polyline.extend({ statics : { TYPE : 'polygon' }, Poly : L.Polygon, options : { showArea : true, shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; L.Draw.Polyline.prototype.initialize.call(this, map, options); // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Polygon.TYPE; }, _updateFinishHandler : function() { var markerCount = this._markers.length; // The first marker should have a click handler to close the polygon if (markerCount === 1) { this._markers[0].on('click', this._finishShape, this); } // Add and update the double click handler if (markerCount > 2) { this._markers[markerCount - 1].on('dblclick', this._finishShape, this); // Only need to remove handler if has been added before if (markerCount > 3) { this._markers[markerCount - 2].off('dblclick', this._finishShape, this); } } }, _getTooltipText : function() { var text, subtext; if (this._markers.length === 0) { text = L.drawLocal.draw.handlers.polygon.tooltip.start; } else if (this._markers.length < 3) { text = L.drawLocal.draw.handlers.polygon.tooltip.cont; } else { text = L.drawLocal.draw.handlers.polygon.tooltip.end; subtext = this._getMeasurementString(); } return { text : text, subtext : subtext }; }, _getMeasurementString : function() { var area = this._area; if (!area) { return null; } return L.GeometryUtil.readableArea(area, this.options.metric); }, _shapeIsValid : function() { return this._markers.length >= 3; }, _vertexAdded : function() { // Check to see if we should show the area if (this.options.allowIntersection || !this.options.showArea) { return; } var latLngs = this._poly.getLatLngs(); this._area = L.GeometryUtil.geodesicArea(latLngs); }, _finishShape : function() { var myUnit; var latLngsArray = this._poly.getLatLngs(); var area = this._area / 1000000; if (area >= 10000) { area = area / 10000; myUnit = '万平方千米'; } else { myUnit = '平方千米'; } var myIcon = L.divIcon({ iconSize : L.point(130, 40), iconAnchor : L.point(50, 25), html : '
                            ' + '面积:' + area.toFixed(2) + myUnit + '
                            ' }); var tipPloygon = L.marker(latLngsArray[latLngsArray.length - 1], { icon : myIcon }).addTo(this._map); this._tipMarkerGroup.addLayer(tipPloygon); var intersects = this._poly.newLatLngIntersects(this._poly .getLatLngs()[0], true); if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { this._showErrorTooltip(); return; } this._fireCreatedEvent(); this.disable(); if (this.options.repeatMode) { this.enable(); } }, // 点击取消清除多边形 _cleanUpShape : function() { // 如果不是正常绘制完成的话,删除已经添加的tipMarker if (!this._drawStatus) { if (this._tipMarkerGroup) { this._map.removeLayer(this._tipMarkerGroup); } } var markerCount = this._markers.length; if (markerCount > 0) { this._markers[0].off('click', this._finishShape, this); if (markerCount > 2) { this._markers[markerCount - 1].off('dblclick', this._finishShape, this); } } } }); L.SimpleShape = {}; L.Draw.SimpleShape = L.Draw.Feature .extend({ options : { repeatMode : false }, initialize : function(map, options) { this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end; L.Draw.Feature.prototype.initialize .call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } // 清空标点数据 if (L.Draw.tempData1) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData1.tipMarkerGroup); L.Draw.tempData1.layer.clearLayers(); } L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { // 初始化绘制完成状态为false; this._drawStatus = false; // 扩展的属性 this._tipMarkerGroup = new L.LayerGroup(); this._map.addLayer(this._tipMarkerGroup); // 如果不是正常绘制完成的话,删除已经添加的tipMarker if (!this._drawStatus) { this._map.removeLayer(this._tipMarkerGroup); } this._map.dragging.disable(); // TODO refactor: move cursor to styles this._container.style.cursor = 'crosshair'; this._tooltip.updateContent({ text : this._initialLabelText }); this._map.on('mousedown', this._onMouseDown, this).on( 'mousemove', this._onMouseMove, this); } }, removeHooks : function() { L.Draw.Feature.prototype.removeHooks.call(this); if (this._map) { this._map.dragging.enable(); // TODO refactor: move cursor to styles this._container.style.cursor = ''; this._map.off('mousedown', this._onMouseDown, this) .off('mousemove', this._onMouseMove, this); L.DomEvent.off(document, 'mouseup', this._onMouseUp); // If the box element doesn't exist they must not have // moved the mouse, so don't need to destroy/return if (this._shape) { this._map.removeLayer(this._shape); delete this._shape; } } this._isDrawing = false; }, _onMouseDown : function(e) { this._isDrawing = true; this._startLatLng = e.latlng; L.DomEvent.on(document, 'mouseup', this._onMouseUp, this) .preventDefault(e.originalEvent); }, _onMouseMove : function(e) { var latlng = e.latlng; this._tooltip.updatePosition(latlng); if (this._isDrawing) { this._tooltip.updateContent({ text : this._endLabelText }); this._drawShape(latlng); } }, _onMouseUp : function() { if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Rectangle = L.Draw.SimpleShape .extend({ statics : { TYPE : 'rectangle' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true } }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as // cannot do this.TYPE :( this.type = L.Draw.Rectangle.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start; // 自定义的 this._RectangleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._RectangleLayerGroup); L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, _drawShape : function(latlng) { if (!this._shape) { this._shape = new L.Rectangle(new L.LatLngBounds( this._startLatLng, latlng), this.options.shapeOptions); this._map.addLayer(this._shape); } else { this._shape.setBounds(new L.LatLngBounds( this._startLatLng, latlng)); } }, _fireCreatedEvent : function() { this._drawStatus = true; var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions); L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle); this._RectangleLayerGroup.addLayer(rectangle); L.Draw.tempData = { tipMarkerGroup : this._tipMarkerGroup, layer : this._RectangleLayerGroup } }, _onMouseUp : function() { var myUnit; var obj = new Array(); obj[0] = this._shape.getBounds()._northEast; obj[2] = this._shape.getBounds()._southWest; obj[1] = L.latLng(this._shape.getBounds()._southWest.lat, this._shape.getBounds()._northEast.lng); obj[3] = L.latLng(this._shape.getBounds()._northEast.lat, this._shape.getBounds()._southWest.lng); var area = (L.GeometryUtil.geodesicArea(obj) / 1000000) .toFixed(2); if (area >= 10000) { area = (area / 10000).toFixed(2); myUnit = '万平方千米'; } else { myUnit = '平方千米'; } var myIcon = L.divIcon({ iconSize : L.point(130, 40), iconAnchor : L.point(65, 20), html : '
                            面积:' + area + myUnit + ' 
                            ' }); var tipRectangle = L.marker(this._shape.getBounds().getCenter(), { icon : myIcon }).addTo(this._map); this._RectangleLayerGroup.addLayer(tipRectangle); if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Circle = L.Draw.SimpleShape .extend({ statics : { TYPE : 'circle' }, options : { shapeOptions : { stroke : true, color : '#f06eaa', weight : 4, opacity : 0.5, fill : true, fillColor : null, // same as color by default fillOpacity : 0.2, clickable : true }, showRadius : true, metric : true // Whether to use the metric meaurement system or imperial }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as // cannot do this.TYPE :( this.type = L.Draw.Circle.TYPE; this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; // 自定义的 this._CircleLayerGroup = new L.FeatureGroup(); this._map.addLayer(this._CircleLayerGroup); L.Draw.SimpleShape.prototype.initialize.call(this, map, options); }, _drawShape : function(latlng) { if (!this._shape) { this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions); this._map.addLayer(this._shape); } else { this._shape.setRadius(this._startLatLng .distanceTo(latlng)); } }, _fireCreatedEvent : function() { this._drawStatus = true; var circle = new L.Circle(this._startLatLng, this._shape .getRadius(), this.options.shapeOptions); L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle); this._CircleLayerGroup.addLayer(circle); L.Draw.tempData = { tipMarkerGroup : this._tipMarkerGroup, layer : this._CircleLayerGroup } }, _onMouseMove : function(e) { var latlng = e.latlng, metric = this.options.metric, showRadius = this.options.showRadius, useMetric = this.options.metric, radius; this._tooltip.updatePosition(latlng); if (this._isDrawing) { this._drawShape(latlng); // Get the new radius (rounded to 1 dp) radius = this._shape.getRadius().toFixed(1); this._tooltip.updateContent({ text : this._endLabelText, subtext : showRadius ? '半径: ' + L.GeometryUtil.readableDistance(radius, useMetric) : '' }); } }, _onMouseUp : function() { var metric = this.options.metric, showRadius = this.options.showRadius, useMetric = this.options.metric, radius, myUnit; // alert(this.getlatLng); radius = this._shape.getRadius().toFixed(1); var r = 0;// 半径 var unit = "";// 单位 // if(showRadius){ bjStr = L.GeometryUtil.readableDistance(radius, useMetric); if (radius > 1000) { r = (radius / 1000).toFixed(2); unit = "km"; } else { r = Math.ceil(radius); unit = "m"; } var area = (3.1415926 * r * r).toFixed(2);// 面积 if (unit == 'km') { if (area >= 10000) { area = (area / 10000).toFixed(2); myUnit = '万平方千米'; } else { myUnit = '平方千米'; } } else { myUnit = '平方米'; } var myIcon = L.divIcon({ iconSize : L.point(160, 40), iconAnchor : L.point(50, 25), html : '
                            ' + '半径:' + bjStr + '
                            面积:' + area + myUnit + '
                            ' }); if (radius != 0) { var tipCircle = L.marker(this._shape.getLatLng(), { icon : myIcon }).addTo(this._map); this._CircleLayerGroup.addLayer(tipCircle); } // } if (this._shape) { this._fireCreatedEvent(); } this.disable(); if (this.options.repeatMode) { this.enable(); } } }); L.Draw.Marker = L.Draw.Feature.extend({ statics : { TYPE : 'marker' }, options : { icon : new L.Icon.Default(), repeatMode : false, zIndexOffset : 2000 // This should be > than the highest z-index any markers }, initialize : function(map, options) { this._map = map; // Save the type so super can fire, need to do this as cannot do // this.TYPE :( this.type = L.Draw.Marker.TYPE; // 自定义的 this._drawnItems = new L.FeatureGroup(); this._map.addLayer(this._drawnItems); L.Draw.Feature.prototype.initialize.call(this, map, options); }, addHooks : function() { // 清空数据 if (L.Draw.tempData) { // tipMarkerGroup : this._tipMarkerGroup, // layer : this._drawnItems this._map.removeLayer(L.Draw.tempData.tipMarkerGroup); L.Draw.tempData.layer.clearLayers(); } L.Draw.Feature.prototype.addHooks.call(this); if (this._map) { // 初始化绘制完成状态为false; this._drawStatus = false; // 扩展的属性 this._tipMarkerGroup = new L.LayerGroup(); this._map.addLayer(this._tipMarkerGroup); // 如果不是正常绘制完成的话,删除已经添加的tipMarker if (!this._drawStatus) { this._map.removeLayer(this._tipMarkerGroup); } this._tooltip.updateContent({ text : L.drawLocal.draw.handlers.marker.tooltip.start }); // Same mouseMarker as in Draw.Polyline if (!this._mouseMarker) { this._mouseMarker = L.marker(this._map.getCenter(), { icon : L.divIcon({ className : 'leaflet-mouse-marker', iconAnchor : [ 20, 20 ], iconSize : [ 40, 40 ] }), opacity : 0, zIndexOffset : this.options.zIndexOffset }); } this._mouseMarker.on('click', this._onClick, this).addTo( this._map); this._map.on('mousemove', this._onMouseMove, this); } }, removeHooks : function() { L.Draw.Feature.prototype.removeHooks.call(this); if (this._map) { if (this._marker) { this._marker.off('click', this._onClick, this); this._map.off('click', this._onClick, this).removeLayer( this._marker); delete this._marker; } this._mouseMarker.off('click', this._onClick, this); this._map.removeLayer(this._mouseMarker); delete this._mouseMarker; this._map.off('mousemove', this._onMouseMove, this); } }, _onMouseMove : function(e) { var latlng = e.latlng; this._tooltip.updatePosition(latlng); this._mouseMarker.setLatLng(latlng); if (!this._marker) { this._marker = new L.Marker(latlng, { icon : this.options.icon, zIndexOffset : this.options.zIndexOffset }); // Bind to both marker and map to make sure we get the click // event. this._marker.on('click', this._onClick, this); this._map.on('click', this._onClick, this).addLayer( this._marker); } else { latlng = this._mouseMarker.getLatLng(); this._marker.setLatLng(latlng); } }, _onClick : function() { this._fireCreatedEvent(); this.disable(); if (this.options.repeatMode) { this.enable(); } }, _fireCreatedEvent : function() { this._drawStatus = true; var myIcon = L.divIcon({ iconSize : L.point(100, 40), iconAnchor : L.point(2, 3), html : '
                            经度:' + this._marker.getLatLng().lng.toFixed(2) + '
                            纬度:' + this._marker.getLatLng().lat.toFixed(2) + ' 
                            ' }); var tipMark = L.marker(this._marker.getLatLng(), { icon : myIcon }).addTo(this._map); this._drawnItems.addLayer(tipMark); var marker = new L.Marker(this._marker.getLatLng(), { icon : this.options.icon }); L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker); this._drawnItems.addLayer(marker); L.Draw.tempData1 = { tipMarkerGroup : this._tipMarkerGroup, layer : this._drawnItems } } }); // 重置 L.Edit = L.Edit || {}; /* * L.Edit.Poly is an editing handler for polylines and polygons. */ L.Edit.Poly = L.Handler .extend({ options : { icon : new L.DivIcon({ iconSize : new L.Point(8, 8), className : 'leaflet-div-icon leaflet-editing-icon' }) }, initialize : function(poly, options) { this._poly = poly; L.setOptions(this, options); }, addHooks : function() { if (this._poly._map) { if (!this._markerGroup) { this._initMarkers(); } this._poly._map.addLayer(this._markerGroup); } }, removeHooks : function() { if (this._poly._map) { this._poly._map.removeLayer(this._markerGroup); delete this._markerGroup; delete this._markers; } }, updateMarkers : function() { this._markerGroup.clearLayers(); this._initMarkers(); }, _initMarkers : function() { if (!this._markerGroup) { this._markerGroup = new L.LayerGroup(); } this._markers = []; var latlngs = this._poly._latlngs, i, j, len, marker; // TODO refactor holes implementation in Polygon to support // it here for (i = 0, len = latlngs.length; i < len; i++) { marker = this._createMarker(latlngs[i], i); marker.on('click', this._onMarkerClick, this); this._markers.push(marker); } var markerLeft, markerRight; for (i = 0, j = len - 1; i < len; j = i++) { if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) { continue; } markerLeft = this._markers[j]; markerRight = this._markers[i]; this._createMiddleMarker(markerLeft, markerRight); this._updatePrevNext(markerLeft, markerRight); } }, _createMarker : function(latlng, index) { var marker = new L.Marker(latlng, { draggable : true, icon : this.options.icon }); marker._origLatLng = latlng; marker._index = index; marker.on('drag', this._onMarkerDrag, this); marker.on('dragend', this._fireEdit, this); this._markerGroup.addLayer(marker); return marker; }, _removeMarker : function(marker) { var i = marker._index; this._markerGroup.removeLayer(marker); this._markers.splice(i, 1); this._poly.spliceLatLngs(i, 1); this._updateIndexes(i, -1); marker.off('drag', this._onMarkerDrag, this).off('dragend', this._fireEdit, this).off('click', this._onMarkerClick, this); }, _fireEdit : function() { this._poly.edited = true; this._poly.fire('edit'); }, _onMarkerDrag : function(e) { var marker = e.target; L.extend(marker._origLatLng, marker._latlng); if (marker._middleLeft) { marker._middleLeft.setLatLng(this._getMiddleLatLng( marker._prev, marker)); } if (marker._middleRight) { marker._middleRight.setLatLng(this._getMiddleLatLng( marker, marker._next)); } this._poly.redraw(); }, _onMarkerClick : function(e) { var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3, marker = e.target; // If removing this point would create an invalid // polyline/polygon don't remove if (this._poly._latlngs.length < minPoints) { return; } // remove the marker this._removeMarker(marker); // update prev/next links of adjacent markers this._updatePrevNext(marker._prev, marker._next); // remove ghost markers near the removed marker if (marker._middleLeft) { this._markerGroup.removeLayer(marker._middleLeft); } if (marker._middleRight) { this._markerGroup.removeLayer(marker._middleRight); } // create a ghost marker in place of the removed one if (marker._prev && marker._next) { this._createMiddleMarker(marker._prev, marker._next); } else if (!marker._prev) { marker._next._middleLeft = null; } else if (!marker._next) { marker._prev._middleRight = null; } this._fireEdit(); }, _updateIndexes : function(index, delta) { this._markerGroup.eachLayer(function(marker) { if (marker._index > index) { marker._index += delta; } }); }, _createMiddleMarker : function(marker1, marker2) { var latlng = this._getMiddleLatLng(marker1, marker2), marker = this ._createMarker(latlng), onClick, onDragStart, onDragEnd; marker.setOpacity(0.6); marker1._middleRight = marker2._middleLeft = marker; onDragStart = function() { var i = marker2._index; marker._index = i; marker.off('click', onClick, this).on('click', this._onMarkerClick, this); latlng.lat = marker.getLatLng().lat; latlng.lng = marker.getLatLng().lng; this._poly.spliceLatLngs(i, 0, latlng); this._markers.splice(i, 0, marker); marker.setOpacity(1); this._updateIndexes(i, 1); marker2._index++; this._updatePrevNext(marker1, marker); this._updatePrevNext(marker, marker2); }; onDragEnd = function() { marker.off('dragstart', onDragStart, this); marker.off('dragend', onDragEnd, this); this._createMiddleMarker(marker1, marker); this._createMiddleMarker(marker, marker2); }; onClick = function() { onDragStart.call(this); onDragEnd.call(this); this._fireEdit(); }; marker.on('click', onClick, this).on('dragstart', onDragStart, this).on('dragend', onDragEnd, this); this._markerGroup.addLayer(marker); }, _updatePrevNext : function(marker1, marker2) { if (marker1) { marker1._next = marker2; } if (marker2) { marker2._prev = marker1; } }, _getMiddleLatLng : function(marker1, marker2) { var map = this._poly._map, p1 = map.project(marker1 .getLatLng()), p2 = map .project(marker2.getLatLng()); return map.unproject(p1._add(p2)._divideBy(2)); } }); L.Polyline.addInitHook(function() { // Check to see if handler has already been initialized. This is to // support versions of Leaflet that still have L.Handler.PolyEdit if (this.editing) { return; } if (L.Edit.Poly) { this.editing = new L.Edit.Poly(this); if (this.options.editable) { this.editing.enable(); } } this.on('add', function() { if (this.editing && this.editing.enabled()) { this.editing.addHooks(); } }); this.on('remove', function() { if (this.editing && this.editing.enabled()) { this.editing.removeHooks(); } }); }); L.Edit = L.Edit || {}; L.Edit.SimpleShape = L.Handler .extend({ options : { moveIcon : new L.DivIcon( { iconSize : new L.Point(8, 8), className : 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move' }), resizeIcon : new L.DivIcon( { iconSize : new L.Point(8, 8), className : 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize' }) }, initialize : function(shape, options) { this._shape = shape; L.Util.setOptions(this, options); }, addHooks : function() { if (this._shape._map) { this._map = this._shape._map; if (!this._markerGroup) { this._initMarkers(); } this._map.addLayer(this._markerGroup); } }, removeHooks : function() { if (this._shape._map) { this._unbindMarker(this._moveMarker); for ( var i = 0, l = this._resizeMarkers.length; i < l; i++) { this._unbindMarker(this._resizeMarkers[i]); } this._resizeMarkers = null; this._map.removeLayer(this._markerGroup); delete this._markerGroup; } this._map = null; }, updateMarkers : function() { this._markerGroup.clearLayers(); this._initMarkers(); }, _initMarkers : function() { if (!this._markerGroup) { this._markerGroup = new L.LayerGroup(); } // Create center marker this._createMoveMarker(); // Create edge marker this._createResizeMarker(); }, _createMoveMarker : function() { // Children override }, _createResizeMarker : function() { // Children override }, _createMarker : function(latlng, icon) { var marker = new L.Marker(latlng, { draggable : true, icon : icon, zIndexOffset : 10 }); this._bindMarker(marker); this._markerGroup.addLayer(marker); return marker; }, _bindMarker : function(marker) { marker.on('dragstart', this._onMarkerDragStart, this).on( 'drag', this._onMarkerDrag, this).on('dragend', this._onMarkerDragEnd, this); }, _unbindMarker : function(marker) { marker.off('dragstart', this._onMarkerDragStart, this).off( 'drag', this._onMarkerDrag, this).off('dragend', this._onMarkerDragEnd, this); }, _onMarkerDragStart : function(e) { var marker = e.target; marker.setOpacity(0); }, _fireEdit : function() { this._shape.edited = true; this._shape.fire('edit'); }, _onMarkerDrag : function(e) { var marker = e.target, latlng = marker.getLatLng(); if (marker === this._moveMarker) { this._move(latlng); } else { this._resize(latlng); } this._shape.redraw(); }, _onMarkerDragEnd : function(e) { var marker = e.target; marker.setOpacity(1); this._fireEdit(); }, _move : function() { // Children override }, _resize : function() { // Children override } }); L.Edit = L.Edit || {}; L.Edit.Rectangle = L.Edit.SimpleShape .extend({ _createMoveMarker : function() { var bounds = this._shape.getBounds(), center = bounds .getCenter(); this._moveMarker = this._createMarker(center, this.options.moveIcon); }, _createResizeMarker : function() { var corners = this._getCorners(); this._resizeMarkers = []; for ( var i = 0, l = corners.length; i < l; i++) { this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon)); // Monkey in the corner index as we will need to know // this for dragging this._resizeMarkers[i]._cornerIndex = i; } }, _onMarkerDragStart : function(e) { L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e); // Save a reference to the opposite point var corners = this._getCorners(), marker = e.target, currentCornerIndex = marker._cornerIndex; this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; this._toggleCornerMarkers(0, currentCornerIndex); }, _onMarkerDragEnd : function(e) { var marker = e.target, bounds, center; // Reset move marker position to the center if (marker === this._moveMarker) { bounds = this._shape.getBounds(); center = bounds.getCenter(); marker.setLatLng(center); } this._toggleCornerMarkers(1); this._repositionCornerMarkers(); L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e); }, _move : function(newCenter) { var latlngs = this._shape.getLatLngs(), bounds = this._shape .getBounds(), center = bounds.getCenter(), offset, newLatLngs = []; // Offset the latlngs to the new center for ( var i = 0, l = latlngs.length; i < l; i++) { offset = [ latlngs[i].lat - center.lat, latlngs[i].lng - center.lng ]; newLatLngs.push([ newCenter.lat + offset[0], newCenter.lng + offset[1] ]); } this._shape.setLatLngs(newLatLngs); // Reposition the resize markers this._repositionCornerMarkers(); }, _resize : function(latlng) { var bounds; // Update the shape based on the current position of this // corner and the opposite point this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner)); // Reposition the move marker bounds = this._shape.getBounds(); this._moveMarker.setLatLng(bounds.getCenter()); }, _getCorners : function() { var bounds = this._shape.getBounds(), nw = bounds .getNorthWest(), ne = bounds.getNorthEast(), se = bounds .getSouthEast(), sw = bounds.getSouthWest(); return [ nw, ne, se, sw ]; }, _toggleCornerMarkers : function(opacity) { for ( var i = 0, l = this._resizeMarkers.length; i < l; i++) { this._resizeMarkers[i].setOpacity(opacity); } }, _repositionCornerMarkers : function() { var corners = this._getCorners(); for ( var i = 0, l = this._resizeMarkers.length; i < l; i++) { this._resizeMarkers[i].setLatLng(corners[i]); } } }); L.Rectangle.addInitHook(function() { if (L.Edit.Rectangle) { this.editing = new L.Edit.Rectangle(this); if (this.options.editable) { this.editing.enable(); } } }); L.Edit = L.Edit || {}; L.Edit.Circle = L.Edit.SimpleShape .extend({ _createMoveMarker : function() { var center = this._shape.getLatLng(); this._moveMarker = this._createMarker(center, this.options.moveIcon); }, _createResizeMarker : function() { var center = this._shape.getLatLng(), resizemarkerPoint = this ._getResizeMarkerPoint(center); this._resizeMarkers = []; this._resizeMarkers.push(this._createMarker( resizemarkerPoint, this.options.resizeIcon)); }, _getResizeMarkerPoint : function(latlng) { // From L.shape.getBounds() var delta = this._shape._radius * Math.cos(Math.PI / 4), point = this._map .project(latlng); return this._map.unproject([ point.x + delta, point.y - delta ]); }, _move : function(latlng) { var resizemarkerPoint = this._getResizeMarkerPoint(latlng); // Move the resize marker this._resizeMarkers[0].setLatLng(resizemarkerPoint); // Move the circle this._shape.setLatLng(latlng); }, _resize : function(latlng) { var moveLatLng = this._moveMarker.getLatLng(), radius = moveLatLng .distanceTo(latlng); this._shape.setRadius(radius); } }); L.Circle.addInitHook(function() { if (L.Edit.Circle) { this.editing = new L.Edit.Circle(this); if (this.options.editable) { this.editing.enable(); } } this.on('add', function() { if (this.editing && this.editing.enabled()) { this.editing.addHooks(); } }); this.on('remove', function() { if (this.editing && this.editing.enabled()) { this.editing.removeHooks(); } }); }); /* * L.LatLngUtil contains different utility functions for LatLngs. */ L.LatLngUtil = { // Clones a LatLngs[], returns [][] cloneLatLngs : function(latlngs) { var clone = []; for ( var i = 0, l = latlngs.length; i < l; i++) { clone.push(this.cloneLatLng(latlngs[i])); } return clone; }, cloneLatLng : function(latlng) { return L.latLng(latlng.lat, latlng.lng); } }; L.GeometryUtil = L .extend( L.GeometryUtil || {}, { // Ported from the OpenLayers implementation. See // https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 geodesicArea : function(latLngs) { var pointsCount = latLngs.length, area = 0.0, d2r = L.LatLng.DEG_TO_RAD, p1, p2; if (pointsCount > 2) { for ( var i = 0; i < pointsCount; i++) { p1 = latLngs[i]; p2 = latLngs[(i + 1) % pointsCount]; area += ((p2.lng - p1.lng) * d2r) * (2 + Math.sin(p1.lat * d2r) + Math .sin(p2.lat * d2r)); } area = area * 6378137.0 * 6378137.0 / 2.0; } return Math.abs(area); }, readableArea : function(area, isMetric) { var areaStr; if (isMetric) { if (area >= 10000) { temp = (area * 0.000001).toFixed(2); if (temp >= 10000) { areaStr = (temp / 10000).toFixed(2) + '万平方千米'; } else { areaStr = temp + '平方千米'; } } else { areaStr = area.toFixed(2) + ' 平方米'; } } else { area *= 0.836127; // Square yards in 1 meter if (area >= 3097600) { // 3097600 square yards // in 1 square mile areaStr = (area / 3097600).toFixed(2) + ' mi²'; } else if (area >= 4840) {// 48040 square // yards in 1 acre areaStr = (area / 4840).toFixed(2) + ' acres'; } else { areaStr = Math.ceil(area) + ' yd²'; } } return areaStr; }, readableDistance : function(distance, isMetric) { var distanceStr; if (isMetric) { // show metres when distance is < 1km, then show // km if (distance > 1000) { distanceStr = (distance / 1000).toFixed(2) + ' 千米'; } else { distanceStr = Math.ceil(distance) + ' 米'; } } else { distance *= 1.09361; if (distance > 1760) { a = ((distance / 1760) * 1609.344) .toFixed(2); if (a < 1000) { distanceStr = a + ' 米'; } else { distanceStr = (a / 1000).toFixed(2) + ' 千米'; } } else { b = Math.ceil(distance) * 0.914; b = b.toFixed(2); if (b < 1000) { distanceStr = b + ' 米'; } else { distanceStr = (b / 1000).toFixed(2) + ' 千米'; } } } return distanceStr; } }); L.Util.extend(L.LineUtil, { // Checks to see if two line segments intersect. Does not handle // degenerate cases. // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf segmentsIntersect : function(/* Point */p, /* Point */p1, /* Point */ p2, /* Point */ p3) { return this._checkCounterclockwise(p, p2, p3) !== this ._checkCounterclockwise(p1, p2, p3) && this._checkCounterclockwise(p, p1, p2) !== this ._checkCounterclockwise(p, p1, p3); }, // check to see if points are in counterclockwise order _checkCounterclockwise : function(/* Point */p, /* Point */p1, /* Point */ p2) { return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x); } }); L.Polyline .include({ // Check to see if this polyline has any linesegments that // intersect. // NOTE: does not support detecting intersection for degenerate // cases. intersects : function() { var points = this._originalPoints, len = points ? points.length : 0, i, p, p1; if (this._tooFewPointsForIntersection()) { return false; } for (i = len - 1; i >= 3; i--) { p = points[i - 1]; p1 = points[i]; if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) { return true; } } return false; }, // Check for intersection if new latlng was added to this // polyline. // NOTE: does not support detecting intersection for degenerate // cases. newLatLngIntersects : function(latlng, skipFirst) { // Cannot check a polyline for intersecting lats/lngs when // not added to the map if (!this._map) { return false; } return this.newPointIntersects(this._map .latLngToLayerPoint(latlng), skipFirst); }, // Check for intersection if new point was added to this // polyline. // newPoint must be a layer point. // NOTE: does not support detecting intersection for degenerate // cases. newPointIntersects : function(newPoint, skipFirst) { var points = this._originalPoints, len = points ? points.length : 0, lastPoint = points ? points[len - 1] : null, // The previous previous line segment. Previous line segment // doesn't need testing. maxIndex = len - 2; if (this._tooFewPointsForIntersection(1)) { return false; } return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0); }, // Polylines with 2 sides can only intersect in cases where // points are collinear (we don't support detecting these). // Cannot have intersection when < 3 line segments (< 4 points) _tooFewPointsForIntersection : function(extraPoints) { var points = this._originalPoints, len = points ? points.length : 0; // Increment length by extraPoints if present len += extraPoints || 0; return !this._originalPoints || len <= 3; }, // Checks a line segment intersections with any line segments // before its predecessor. // Don't need to check the predecessor as will never intersect. _lineSegmentsIntersectsRange : function(p, p1, maxIndex, minIndex) { var points = this._originalPoints, p2, p3; minIndex = minIndex || 0; // Check all previous line segments (beside the immediately // previous) for intersections for ( var j = maxIndex; j > minIndex; j--) { p2 = points[j - 1]; p3 = points[j]; if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) { return true; } } return false; } }); L.Polygon .include({ // Checks a polygon for any intersecting line segments. Ignores // holes. intersects : function() { var polylineIntersects, points = this._originalPoints, len, firstPoint, lastPoint, maxIndex; if (this._tooFewPointsForIntersection()) { return false; } polylineIntersects = L.Polyline.prototype.intersects .call(this); // If already found an intersection don't need to check for // any more. if (polylineIntersects) { return true; } len = points.length; firstPoint = points[0]; lastPoint = points[len - 1]; maxIndex = len - 2; // Check the line segment between last and first point. // Don't need to check the first line segment (minIndex = 1) return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1); } }); L.Control.Draw = L.Control .extend({ options : { position : 'topleft', draw : {}, edit : false }, initialize : function(options) { if (L.version < "0.7") { throw new Error( 'Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/'); } L.Control.prototype.initialize.call(this, options); var id, toolbar; this._toolbars = {}; // Initialize toolbars if (L.DrawToolbar && this.options.draw) { toolbar = new L.DrawToolbar(this.options.draw); id = L.stamp(toolbar); this._toolbars[id] = toolbar; // Listen for when toolbar is enabled this._toolbars[id].on('enable', this._toolbarEnabled, this); } if (L.EditToolbar && this.options.edit) { toolbar = new L.EditToolbar(this.options.edit); id = L.stamp(toolbar); this._toolbars[id] = toolbar; // Listen for when toolbar is enabled this._toolbars[id].on('enable', this._toolbarEnabled, this); } }, onAdd : function(map) { var container = L.DomUtil.create('div', 'leaflet-draw'), addedTopClass = false, topClassName = 'leaflet-draw-toolbar-top', toolbarContainer; for ( var toolbarId in this._toolbars) { if (this._toolbars.hasOwnProperty(toolbarId)) { toolbarContainer = this._toolbars[toolbarId] .addToolbar(map); // Add class to the first toolbar to remove the // margin if (!addedTopClass) { if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) { L.DomUtil.addClass( toolbarContainer.childNodes[0], topClassName); } addedTopClass = true; } container.appendChild(toolbarContainer); } } return container; }, onRemove : function() { for ( var toolbarId in this._toolbars) { if (this._toolbars.hasOwnProperty(toolbarId)) { this._toolbars[toolbarId].removeToolbar(); } } }, setDrawingOptions : function(options) { for ( var toolbarId in this._toolbars) { if (this._toolbars[toolbarId] instanceof L.DrawToolbar) { this._toolbars[toolbarId].setOptions(options); } } }, _toolbarEnabled : function(e) { var id = '' + L.stamp(e.target); for ( var toolbarId in this._toolbars) { if (this._toolbars.hasOwnProperty(toolbarId) && toolbarId !== id) { this._toolbars[toolbarId].disable(); } } } }); L.Map.mergeOptions({ drawControlTooltips : true, drawControl : false }); L.Map.addInitHook(function() { if (this.options.drawControl) { this.drawControl = new L.Control.Draw(); this.addControl(this.drawControl); } }); L.Toolbar = L.Class .extend({ includes : [ L.Mixin.Events ], initialize : function(options) { L.setOptions(this, options); this._modes = {}; this._actionButtons = []; this._activeMode = null; }, enabled : function() { return this._activeMode !== null; }, disable : function() { if (!this.enabled()) { return; } this._activeMode.handler.disable(); }, removeToolbar : function() { // Dispose each handler for ( var handlerId in this._modes) { if (this._modes.hasOwnProperty(handlerId)) { // Unbind handler button this._disposeButton(this._modes[handlerId].button, this._modes[handlerId].handler.enable); // Make sure is disabled this._modes[handlerId].handler.disable(); // Unbind handler this._modes[handlerId].handler.off('enabled', this._handlerActivated, this).off( 'disabled', this._handlerDeactivated, this); } } this._modes = {}; // Dispose the actions toolbar for ( var i = 0, l = this._actionButtons.length; i < l; i++) { this._disposeButton(this._actionButtons[i].button, this._actionButtons[i].callback); } this._actionButtons = []; this._actionsContainer = null; }, _initModeHandler : function(handler, container, buttonIndex, classNamePredix, buttonTitle) { var type = handler.type; this._modes[type] = {}; this._modes[type].handler = handler; this._modes[type].button = this._createButton({ title : buttonTitle, className : classNamePredix + '-' + type, container : container, callback : this._modes[type].handler.enable, context : this._modes[type].handler }); this._modes[type].buttonIndex = buttonIndex; this._modes[type].handler.on('enabled', this._handlerActivated, this).on('disabled', this._handlerDeactivated, this); }, _createButton : function(options) { var link = L.DomUtil.create('a', options.className || '', options.container); link.href = '#'; if (options.text) { link.innerHTML = options.text; } if (options.title) { link.title = options.title; } L.DomEvent.on(link, 'click', L.DomEvent.stopPropagation) .on(link, 'mousedown', L.DomEvent.stopPropagation) .on(link, 'dblclick', L.DomEvent.stopPropagation) .on(link, 'click', L.DomEvent.preventDefault).on( link, 'click', options.callback, options.context); return link; }, _disposeButton : function(button, callback) { L.DomEvent.off(button, 'click', L.DomEvent.stopPropagation) .off(button, 'mousedown', L.DomEvent.stopPropagation).off(button, 'dblclick', L.DomEvent.stopPropagation) .off(button, 'click', L.DomEvent.preventDefault) .off(button, 'click', callback); }, _handlerActivated : function(e) { // Disable active mode (if present) if (this._activeMode && this._activeMode.handler.enabled()) { this._activeMode.handler.disable(); } // Cache new active feature this._activeMode = this._modes[e.handler]; L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); this._showActionsToolbar(); this.fire('enable'); }, _handlerDeactivated : function() { this._hideActionsToolbar(); if (this._activeMode && this._activeMode.button) { L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); } this._activeMode = null; this.fire('disable'); }, _createActions : function(buttons) { var container = L.DomUtil.create('ul', 'leaflet-draw-actions'), l = buttons.length, li, button; for ( var i = 0; i < l; i++) { li = L.DomUtil.create('li', '', container); button = this._createButton({ title : buttons[i].title, text : buttons[i].text, container : li, callback : buttons[i].callback, context : buttons[i].context }); this._actionButtons.push({ button : button, callback : buttons[i].callback }); } return container; }, _showActionsToolbar : function() { var buttonIndex = this._activeMode.buttonIndex, lastButtonIndex = this._lastButtonIndex, buttonHeight = 26, // TODO: // this // should // be // calculated borderHeight = 1, // TODO: this should also be calculated toolbarPosition = (buttonIndex * buttonHeight) + (buttonIndex * borderHeight) - 1; // Correctly position the cancel button this._actionsContainer.style.top = toolbarPosition + 'px'; if (buttonIndex === 0) { L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top'); } if (buttonIndex === lastButtonIndex) { L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); } this._actionsContainer.style.display = 'block'; }, _hideActionsToolbar : function() { this._actionsContainer.style.display = 'none'; L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top'); L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); } }); L.Tooltip = L.Class .extend({ initialize : function(map) { this._map = map; this._popupPane = map._panes.popupPane; this._container = map.options.drawControlTooltips ? L.DomUtil .create('div', 'leaflet-draw-tooltip', this._popupPane) : null; this._singleLineLabel = false; }, dispose : function() { if (this._container) { this._popupPane.removeChild(this._container); this._container = null; } }, updateContent : function(labelText) { if (!this._container) { return this; } labelText.subtext = labelText.subtext || ''; // update the vertical position (only if changed) if (labelText.subtext.length === 0 && !this._singleLineLabel) { L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single'); this._singleLineLabel = true; } else if (labelText.subtext.length > 0 && this._singleLineLabel) { L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single'); this._singleLineLabel = false; } this._container.innerHTML = (labelText.subtext.length > 0 ? '' + labelText.subtext + '' + '
                            ' : '') + '' + labelText.text + ''; return this; }, updatePosition : function(latlng) { var pos = this._map.latLngToLayerPoint(latlng), tooltipContainer = this._container; if (this._container) { tooltipContainer.style.visibility = 'inherit'; L.DomUtil.setPosition(tooltipContainer, pos); } return this; }, showAsError : function() { if (this._container) { L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip'); } return this; }, removeError : function() { if (this._container) { L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip'); } return this; } }); L.DrawToolbar = L.Toolbar .extend({ options : { zoomIn : {}, zoomOut : {}, polyline : {}, polygon : {}, rectangle : {}, circle : {}, polygonQuery : {}, rectangleQuery : {}, circleQuery : {}, marker : {}, clearMap : {}, reset : {}, cleanAll : {} }, initialize : function(options) { // Ensure that the options are merged correctly since // L.extend is only shallow for ( var type in this.options) { if (this.options.hasOwnProperty(type)) { if (options[type]) { options[type] = L.extend({}, this.options[type], options[type]); } } } L.Toolbar.prototype.initialize.call(this, options); }, addToolbar : function(map) { var container = L.DomUtil.create('div', 'leaflet-draw-section'), buttonIndex = 0, buttonClassPrefix = 'leaflet-draw-draw'; this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar'); // 放大 if (this.options.zoomIn) { this._initModeHandler(new L.Draw.ZoomIn(map, this.options.zoomIn), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.zoomIn); } // 缩小 if (this.options.zoomOut) { this._initModeHandler(new L.Draw.ZoomOut(map, this.options.zoomOut), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.zoomOut); } if (this.options.polyline) { this._initModeHandler(new L.Draw.Polyline(map, this.options.polyline), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.polyline); } if (this.options.polygon) { this._initModeHandler(new L.Draw.Polygon(map, this.options.polygon), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.polygon); } if (this.options.rectangle) { this._initModeHandler(new L.Draw.Rectangle(map, this.options.rectangle), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.rectangle); } if (this.options.circle) { this._initModeHandler(new L.Draw.Circle(map, this.options.circle), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.circle); } if (this.options.marker) { this._initModeHandler(new L.Draw.Marker(map, this.options.marker), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.marker); } /* * * 空间查询-王孝胜 * */ // 圆查询 if (this.options.circleQuery) { this._initModeHandler(new L.Draw.Circle.Query(map, this.options.circleQuery), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.circleQuery); } // 矩形查询 if (this.options.rectangleQuery) { this ._initModeHandler( new L.Draw.Rectangle.Query(map, this.options.rectangleQuery), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.rectangleQuery); } // 多边形查询 if (this.options.polygonQuery) { this._initModeHandler(new L.Draw.Polygon.Query(map, this.options.polygonQuery), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.polygonQuery); } // 重置 if (this.options.reset) { this._initModeHandler(new L.Draw.Reset(map, this.options.reset), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.reset); } // 清空所有地图 if (this.options.cleanAll) { this._initModeHandler(new L.Draw.CleanAll(map, this.options.cleanAll), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.draw.toolbar.buttons.cleanAll); } // Save button index of the last button, -1 as we would have // ++ after the last button this._lastButtonIndex = --buttonIndex; // Create the actions part of the toolbar this._actionsContainer = this._createActions([ { title : L.drawLocal.draw.toolbar.actions.title, text : L.drawLocal.draw.toolbar.actions.text, callback : this.disable, context : this } ]); // Add draw and cancel containers to the control container container.appendChild(this._toolbarContainer); // 控制是否显示取消按钮 // container.appendChild(this._actionsContainer); return container; }, setOptions : function(options) { L.setOptions(this, options); for ( var type in this._modes) { if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) { this._modes[type].handler.setOptions(options[type]); } } } }); /* * L.Map.mergeOptions({ editControl: true }); */ L.EditToolbar = L.Toolbar .extend({ options : { edit : { selectedPathOptions : { color : '#fe57a1', /* Hot pink all the things! */ opacity : 0.6, dashArray : '10, 10', fill : true, fillColor : '#fe57a1', fillOpacity : 0.1 } }, remove : {}, featureGroup : null /* * REQUIRED! TODO: perhaps if not set then all layers on the map * are selectable? */ }, initialize : function(options) { // Need to set this manually since null is an acceptable // value here if (options.edit) { if (typeof options.edit.selectedPathOptions === 'undefined') { options.edit.selectedPathOptions = this.options.edit.selectedPathOptions; } options.edit = L.extend({}, this.options.edit, options.edit); } if (options.remove) { options.remove = L.extend({}, this.options.remove, options.remove); } L.Toolbar.prototype.initialize.call(this, options); this._selectedFeatureCount = 0; }, addToolbar : function(map) { var container = L.DomUtil.create('div', 'leaflet-draw-section'), buttonIndex = 0, buttonClassPrefix = 'leaflet-draw-edit', featureGroup = this.options.featureGroup; this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar'); this._map = map; if (this.options.edit) { this ._initModeHandler( new L.EditToolbar.Edit( map, { featureGroup : featureGroup, selectedPathOptions : this.options.edit.selectedPathOptions }), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.edit.toolbar.buttons.edit); } if (this.options.remove) { this._initModeHandler(new L.EditToolbar.Delete(map, { featureGroup : featureGroup }), this._toolbarContainer, buttonIndex++, buttonClassPrefix, L.drawLocal.edit.toolbar.buttons.remove); } // Save button index of the last button, -1 as we would have // ++ after the last button this._lastButtonIndex = --buttonIndex; // Create the actions part of the toolbar this._actionsContainer = this._createActions([ { title : L.drawLocal.edit.toolbar.actions.save.title, text : L.drawLocal.edit.toolbar.actions.save.text, callback : this._save, context : this }, { title : L.drawLocal.edit.toolbar.actions.cancel.title, text : L.drawLocal.edit.toolbar.actions.cancel.text, callback : this.disable, context : this } ]); // Add draw and cancel containers to the control container container.appendChild(this._toolbarContainer); container.appendChild(this._actionsContainer); this._checkDisabled(); featureGroup.on('layeradd layerremove', this._checkDisabled, this); return container; }, removeToolbar : function() { L.Toolbar.prototype.removeToolbar.call(this); this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this); }, disable : function() { if (!this.enabled()) { return; } this._activeMode.handler.revertLayers(); L.Toolbar.prototype.disable.call(this); }, _save : function() { this._activeMode.handler.save(); this._activeMode.handler.disable(); }, _checkDisabled : function() { var featureGroup = this.options.featureGroup, hasLayers = featureGroup .getLayers().length !== 0, button; if (this.options.edit) { button = this._modes[L.EditToolbar.Edit.TYPE].button; if (hasLayers) { L.DomUtil.removeClass(button, 'leaflet-disabled'); } else { L.DomUtil.addClass(button, 'leaflet-disabled'); } button .setAttribute( 'title', hasLayers ? L.drawLocal.edit.toolbar.buttons.edit : L.drawLocal.edit.toolbar.buttons.editDisabled); } if (this.options.remove) { button = this._modes[L.EditToolbar.Delete.TYPE].button; if (hasLayers) { L.DomUtil.removeClass(button, 'leaflet-disabled'); } else { L.DomUtil.addClass(button, 'leaflet-disabled'); } button .setAttribute( 'title', hasLayers ? L.drawLocal.edit.toolbar.buttons.remove : L.drawLocal.edit.toolbar.buttons.removeDisabled); } } }); L.EditToolbar.Edit = L.Handler .extend({ statics : { TYPE : 'edit' }, includes : L.Mixin.Events, initialize : function(map, options) { L.Handler.prototype.initialize.call(this, map); // Set options to the default unless already set this._selectedPathOptions = options.selectedPathOptions; // Store the selectable layer group for ease of access this._featureGroup = options.featureGroup; if (!(this._featureGroup instanceof L.FeatureGroup)) { throw new Error( 'options.featureGroup must be a L.FeatureGroup'); } this._uneditedLayerProps = {}; // Save the type so super can fire, need to do this as // cannot do this.TYPE :( this.type = L.EditToolbar.Edit.TYPE; }, enable : function() { if (this._enabled || !this._hasAvailableLayers()) { return; } L.Handler.prototype.enable.call(this); this._featureGroup.on('layeradd', this._enableLayerEdit, this).on('layerremove', this._disableLayerEdit, this); this.fire('enabled', { handler : this.type }); this._map.fire('draw:editstart', { handler : this.type }); }, disable : function() { if (!this._enabled) { return; } this.fire('disabled', { handler : this.type }); this._map.fire('draw:editstop', { handler : this.type }); this._featureGroup.off('layeradd', this._enableLayerEdit, this).off('layerremove', this._disableLayerEdit, this); L.Handler.prototype.disable.call(this); }, addHooks : function() { var map = this._map; if (map) { map.getContainer().focus(); this._featureGroup.eachLayer(this._enableLayerEdit, this); this._tooltip = new L.Tooltip(this._map); this._tooltip .updateContent({ text : L.drawLocal.edit.handlers.edit.tooltip.text, subtext : L.drawLocal.edit.handlers.edit.tooltip.subtext }); this._map.on('mousemove', this._onMouseMove, this); } }, removeHooks : function() { if (this._map) { // Clean up selected layers. this._featureGroup.eachLayer(this._disableLayerEdit, this); // Clear the backups of the original layers this._uneditedLayerProps = {}; this._tooltip.dispose(); this._tooltip = null; this._map.off('mousemove', this._onMouseMove, this); } }, revertLayers : function() { this._featureGroup.eachLayer(function(layer) { this._revertLayer(layer); }, this); }, save : function() { var editedLayers = new L.LayerGroup(); this._featureGroup.eachLayer(function(layer) { if (layer.edited) { editedLayers.addLayer(layer); layer.edited = false; } }); this._map.fire('draw:edited', { layers : editedLayers }); }, _backupLayer : function(layer) { var id = L.Util.stamp(layer); if (!this._uneditedLayerProps[id]) { // Polyline, Polygon or Rectangle if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { this._uneditedLayerProps[id] = { latlngs : L.LatLngUtil.cloneLatLngs(layer .getLatLngs()) }; } else if (layer instanceof L.Circle) { this._uneditedLayerProps[id] = { latlng : L.LatLngUtil.cloneLatLng(layer .getLatLng()), radius : layer.getRadius() }; } else { // Marker this._uneditedLayerProps[id] = { latlng : L.LatLngUtil.cloneLatLng(layer .getLatLng()) }; } } }, _revertLayer : function(layer) { var id = L.Util.stamp(layer); layer.edited = false; if (this._uneditedLayerProps.hasOwnProperty(id)) { // Polyline, Polygon or Rectangle if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { layer .setLatLngs(this._uneditedLayerProps[id].latlngs); } else if (layer instanceof L.Circle) { layer .setLatLng(this._uneditedLayerProps[id].latlng); layer .setRadius(this._uneditedLayerProps[id].radius); } else { // Marker layer .setLatLng(this._uneditedLayerProps[id].latlng); } } }, _toggleMarkerHighlight : function(marker) { if (!marker._icon) { return; } // This is quite naughty, but I don't see another way of // doing it. (short of setting a new icon) var icon = marker._icon; icon.style.display = 'none'; if (L.DomUtil .hasClass(icon, 'leaflet-edit-marker-selected')) { L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected'); // Offset as the border will make the icon move. this._offsetMarker(icon, -4); } else { L.DomUtil .addClass(icon, 'leaflet-edit-marker-selected'); // Offset as the border will make the icon move. this._offsetMarker(icon, 4); } icon.style.display = ''; }, _offsetMarker : function(icon, offset) { var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset, iconMarginLeft = parseInt( icon.style.marginLeft, 10) - offset; icon.style.marginTop = iconMarginTop + 'px'; icon.style.marginLeft = iconMarginLeft + 'px'; }, _enableLayerEdit : function(e) { var layer = e.layer || e.target || e, isMarker = layer instanceof L.Marker, pathOptions; // Don't do anything if this layer is a marker but doesn't // have an icon. Markers // should usually have icons. If using Leaflet.draw with // Leafler.markercluster there // is a chance that a marker doesn't. if (isMarker && !layer._icon) { return; } // Back up this layer (if haven't before) this._backupLayer(layer); // Update layer style so appears editable if (this._selectedPathOptions) { pathOptions = L.Util.extend({}, this._selectedPathOptions); if (isMarker) { this._toggleMarkerHighlight(layer); } else { layer.options.previousOptions = layer.options; // Make sure that Polylines are not filled if (!(layer instanceof L.Circle) && !(layer instanceof L.Polygon) && !(layer instanceof L.Rectangle)) { pathOptions.fill = false; } layer.setStyle(pathOptions); } } if (isMarker) { layer.dragging.enable(); layer.on('dragend', this._onMarkerDragEnd); } else { layer.editing.enable(); } }, _disableLayerEdit : function(e) { var layer = e.layer || e.target || e; layer.edited = false; // Reset layer styles to that of before select if (this._selectedPathOptions) { if (layer instanceof L.Marker) { this._toggleMarkerHighlight(layer); } else { // reset the layer style to what is was before being // selected layer.setStyle(layer.options.previousOptions); // remove the cached options for the layer object delete layer.options.previousOptions; } } if (layer instanceof L.Marker) { layer.dragging.disable(); layer.off('dragend', this._onMarkerDragEnd, this); } else { layer.editing.disable(); } }, _onMarkerDragEnd : function(e) { var layer = e.target; layer.edited = true; }, _onMouseMove : function(e) { this._tooltip.updatePosition(e.latlng); }, _hasAvailableLayers : function() { return this._featureGroup.getLayers().length !== 0; } }); L.EditToolbar.Delete = L.Handler .extend({ statics : { TYPE : 'remove' // not delete as delete is reserved in js }, includes : L.Mixin.Events, initialize : function(map, options) { L.Handler.prototype.initialize.call(this, map); L.Util.setOptions(this, options); // Store the selectable layer group for ease of access this._deletableLayers = this.options.featureGroup; if (!(this._deletableLayers instanceof L.FeatureGroup)) { throw new Error( 'options.featureGroup must be a L.FeatureGroup'); } // Save the type so super can fire, need to do this as // cannot do this.TYPE :( this.type = L.EditToolbar.Delete.TYPE; }, enable : function() { if (this._enabled || !this._hasAvailableLayers()) { return; } L.Handler.prototype.enable.call(this); this._deletableLayers.on('layeradd', this._enableLayerDelete, this).on('layerremove', this._disableLayerDelete, this); this.fire('enabled', { handler : this.type }); this._map.fire('draw:editstart', { handler : this.type }); }, disable : function() { if (!this._enabled) { return; } L.Handler.prototype.disable.call(this); this._deletableLayers.off('layeradd', this._enableLayerDelete, this).off('layerremove', this._disableLayerDelete, this); this.fire('disabled', { handler : this.type }); this._map.fire('draw:editstop', { handler : this.type }); }, addHooks : function() { var map = this._map; if (map) { map.getContainer().focus(); this._deletableLayers.eachLayer( this._enableLayerDelete, this); this._deletedLayers = new L.layerGroup(); this._tooltip = new L.Tooltip(this._map); this._tooltip .updateContent({ text : L.drawLocal.edit.handlers.remove.tooltip.text }); this._map.on('mousemove', this._onMouseMove, this); } }, removeHooks : function() { if (this._map) { this._deletableLayers.eachLayer( this._disableLayerDelete, this); this._deletedLayers = null; this._tooltip.dispose(); this._tooltip = null; this._map.off('mousemove', this._onMouseMove, this); } }, revertLayers : function() { // Iterate of the deleted layers and add them back into the // featureGroup this._deletedLayers.eachLayer(function(layer) { this._deletableLayers.addLayer(layer); }, this); }, save : function() { this._map.fire('draw:deleted', { layers : this._deletedLayers }); }, _enableLayerDelete : function(e) { var layer = e.layer || e.target || e; layer.on('click', this._removeLayer, this); }, _disableLayerDelete : function(e) { var layer = e.layer || e.target || e; layer.off('click', this._removeLayer, this); // Remove from the deleted layers so we can't accidently // revert if the user presses cancel this._deletedLayers.removeLayer(layer); }, _removeLayer : function(e) { var layer = e.layer || e.target || e; this._deletableLayers.removeLayer(layer); this._deletedLayers.addLayer(layer); }, _onMouseMove : function(e) { this._tooltip.updatePosition(e.latlng); }, _hasAvailableLayers : function() { return this._deletableLayers.getLayers().length !== 0; } }); }(this, document)); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.draw/leaflet.draw.css ================================================ /* ================================================================== */ /* Toolbars /* ================================================================== */ .leaflet-draw-section { position: relative; } .leaflet-draw-toolbar { margin-top: 12px; } .leaflet-draw-toolbar-top { margin-top: 0; } .leaflet-draw-toolbar-notop a:first-child { border-top-right-radius: 0; } .leaflet-draw-toolbar-nobottom a:last-child { border-bottom-right-radius: 0; } .leaflet-draw-toolbar a { background-image: url('images/spritesheet.png'); background-repeat: no-repeat; } .leaflet-retina .leaflet-draw-toolbar a { background-image: url('images/spritesheet-2x.png'); background-size: 270px 30px; } .leaflet-draw a { display: block; text-align: center; text-decoration: none; } /* ================================================================== */ /* Toolbar actions menu /* ================================================================== */ .leaflet-draw-actions { display: none; list-style: none; margin: 0; padding: 0; position: absolute; left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */ top: 0; margin-top: 1px; white-space: nowrap; } .leaflet-right .leaflet-draw-actions { right:26px; left:auto; } .leaflet-draw-actions li { display: inline-block; } .leaflet-draw-actions li:first-child a { border-left: none; } .leaflet-draw-actions li:last-child a { -webkit-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .leaflet-right .leaflet-draw-actions li:last-child a { -webkit-border-radius: 0; border-radius: 0; } .leaflet-right .leaflet-draw-actions li:first-child a { -webkit-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .leaflet-draw-actions a { background-color: #919187; border-left: 1px solid #AAA; color: #FFF; font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif; line-height: 28px; text-decoration: none; padding-left: 10px; padding-right: 10px; height: 28px; } .leaflet-draw-actions-bottom { margin-top: 0; white-space: nowrap; } .leaflet-draw-actions-top { margin-top: 1px; white-space: nowrap; } .leaflet-draw-actions-top a, .leaflet-draw-actions-bottom a { height: 27px; line-height: 27px; } .leaflet-draw-actions a:hover { background-color: #A0A098; } .leaflet-draw-actions-top.leaflet-draw-actions-bottom a { height: 26px; line-height: 26px; } /* ================================================================== */ /* Draw toolbar /* ================================================================== */ /*放大*/ .leaflet-draw-toolbar .leaflet-draw-draw-zoomIn { background-position: -272px -2px; } /*缩小*/ .leaflet-draw-toolbar .leaflet-draw-draw-zoomOut { background-position: -304px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-polyline { background-position: -2px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-polygon { background-position: -31px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-rectangle { background-position: -62px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-circle { background-position: -92px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-marker { background-position: -122px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-circleQuery { background-position: -92px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-rectangleQuery{ background-position: -62px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-polygonQuery{ background-position: -31px -2px; } .leaflet-draw-toolbar .leaflet-draw-draw-reset{ background-position: -182px -2px; } /* ================================================================== */ /* Edit toolbar /* ================================================================== */ .leaflet-draw-toolbar .leaflet-draw-edit-edit { background-position: -152px -2px; } .leaflet-draw-toolbar .leaflet-draw-edit-remove { background-position: -182px -2px; } .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled { background-position: -212px -2px; } .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled { background-position: -242px -2px; } /* ================================================================== */ /* Drawing styles /* ================================================================== */ .leaflet-mouse-marker { background-color: #fff; cursor: crosshair; } .leaflet-draw-tooltip { background: rgb(54, 54, 54); background: rgba(0, 0, 0, 0.5); border: 1px solid transparent; -webkit-border-radius: 4px; border-radius: 4px; color: #fff; font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif; margin-left: 20px; margin-top: -21px; padding: 4px 8px; position: absolute; visibility: hidden; white-space: nowrap; z-index: 6; } .leaflet-draw-tooltip:before { border-right: 6px solid black; border-right-color: rgba(0, 0, 0, 0.5); border-top: 6px solid transparent; border-bottom: 6px solid transparent; content: ""; position: absolute; top: 7px; left: -7px; } .leaflet-error-draw-tooltip { background-color: #F2DEDE; border: 1px solid #E6B6BD; color: #B94A48; } .leaflet-error-draw-tooltip:before { border-right-color: #E6B6BD; } .leaflet-draw-tooltip-single { margin-top: -12px } .leaflet-draw-tooltip-subtext { color: #f8d5e4; } .leaflet-draw-guide-dash { font-size: 1%; opacity: 0.6; position: absolute; width: 5px; height: 5px; } /* ================================================================== */ /* Edit styles /* ================================================================== */ .leaflet-edit-marker-selected { background: rgba(254, 87, 161, 0.1); border: 4px dashed rgba(254, 87, 161, 0.6); -webkit-border-radius: 4px; border-radius: 4px; } .leaflet-edit-move { cursor: move; } .leaflet-edit-resize { cursor: pointer; } /* ================================================================== */ /* Old IE styles /* ================================================================== */ .leaflet-oldie .leaflet-draw-toolbar { border: 3px solid #999; } .leaflet-oldie .leaflet-draw-toolbar a { background-color: #eee; } .leaflet-oldie .leaflet-draw-toolbar a:hover { background-color: #fff; } .leaflet-oldie .leaflet-draw-actions { left: 1px; margin-top: 3px; } .leaflet-oldie .leaflet-draw-actions li { display: inline; zoom: 1; } .leaflet-oldie .leaflet-edit-marker-selected { border: 4px dashed #fe93c2; } .leaflet-oldie .leaflet-draw-actions a { background-color: #999; } .leaflet-oldie .leaflet-draw-actions a:hover { background-color: #a5a5a5; } .leaflet-oldie .leaflet-draw-actions-top a { margin-top: 1px; } .leaflet-oldie .leaflet-draw-actions-bottom a { height: 28px; line-height: 28px; } .leaflet-oldie .leaflet-draw-actions-top.leaflet-draw-actions-bottom a { height: 27px; line-height: 27px; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.draw/leaflet.draw.js ================================================ /* Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps. (c) 2012-2013, Jacob Toye, Smartrak https://github.com/Leaflet/Leaflet.draw http://leafletjs.com https://github.com/jacobtoye */ (function(t,e){L.drawVersion="0.2.3-dev",L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes.",text:"Save"},cancel:{title:"Cancel editing, discards all changes.",text:"Cancel"}},buttons:{edit:"Edit layers.",editDisabled:"No layers to edit.",remove:"Delete layers.",removeDisabled:"No layers to delete."}},handlers:{edit:{tooltip:{text:"Drag handles, or marker to edit feature.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove"}}}}},L.Draw={},L.Draw.Feature=L.Handler.extend({includes:L.Mixin.Events,initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.Util.extend(this.options,e)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire("draw:drawstart",{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this.fire("disabled",{handler:this.type}),this._map.fire("draw:drawstop",{layerType:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Tooltip(this._map),L.DomEvent.addListener(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.removeListener(this._container,"keyup",this._cancelDrawing))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire("draw:created",{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&this.disable()}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),guidelineDistance:20,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,showLength:!0,zIndexOffset:2e3},initialize:function(t,e){this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mousemove",this._onMouseMove,this).off("zoomend",this._onZoomEnd,this)},_finishShape:function(){var t=this._poly.newLatLngIntersects(this._poly.getLatLngs()[0],!0);return!this.options.allowIntersection&&t||!this._shapeIsValid()?(this._showErrorTooltip(),undefined):(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable(),undefined)},_shapeIsValid:function(){return!0},_onZoomEnd:function(){this._updateGuide()},_onMouseMove:function(t){var e=t.layerPoint,i=t.latlng;this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_onClick:function(t){var e=t.target.getLatLng(),i=this._markers.length;return i>0&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(e)?(this._showErrorTooltip(),undefined):(this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(e)),this._poly.addLatLng(e),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._updateFinishHandler(),this._vertexAdded(e),this._clearGuides(),this._updateTooltip(),undefined)},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers.length;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,a,s,r=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)));for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane)),i=this.options.guidelineDistance;r>i;i+=this.options.guidelineDistance)o=i/r,a={x:Math.floor(t.x*(1-o)+o*e.x),y:Math.floor(t.y*(1-o)+o*e.y)},s=L.DomUtil.create("div","leaflet-draw-guide-dash",this._guidesContainer),s.style.backgroundColor=this._errorShown?this.options.drawError.color:this.options.shapeOptions.color,L.DomUtil.setPosition(s,a)},_updateGuideColor:function(t){if(this._guidesContainer)for(var e=0,i=this._guidesContainer.childNodes.length;i>e;e++)this._guidesContainer.childNodes[e].style.backgroundColor=t},_clearGuides:function(){if(this._guidesContainer)for(;this._guidesContainer.firstChild;)this._guidesContainer.removeChild(this._guidesContainer.firstChild)},_getTooltipText:function(){var t,e,i=this.options.showLength;return 0===this._markers.length?t={text:L.drawLocal.draw.handlers.polyline.tooltip.start}:(e=i?this._getMeasurementString():"",t=1===this._markers.length?{text:L.drawLocal.draw.handlers.polyline.tooltip.cont,subtext:e}:{text:L.drawLocal.draw.handlers.polyline.tooltip.end,subtext:e}),t},_getMeasurementString:function(){var t,e=this._currentLatLng,i=this._markers[this._markers.length-1].getLatLng();return t=this._measurementRunningTotal+e.distanceTo(i),L.GeometryUtil.readableDistance(t,this.options.metric)},_showErrorTooltip:function(){this._errorShown=!0,this._tooltip.showAsError().updateContent({text:this.options.drawError.message}),this._updateGuideColor(this.options.drawError.color),this._poly.setStyle({color:this.options.drawError.color}),this._clearHideErrorTimeout(),this._hideErrorTimeout=setTimeout(L.Util.bind(this._hideErrorTooltip,this),this.options.drawError.timeout)},_hideErrorTooltip:function(){this._errorShown=!1,this._clearHideErrorTimeout(),this._tooltip.removeError().updateContent(this._getTooltipText()),this._updateGuideColor(this.options.shapeOptions.color),this._poly.setStyle({color:this.options.shapeOptions.color})},_clearHideErrorTimeout:function(){this._hideErrorTimeout&&(clearTimeout(this._hideErrorTimeout),this._hideErrorTimeout=null)},_vertexAdded:function(t){1===this._markers.length?this._measurementRunningTotal=0:this._measurementRunningTotal+=t.distanceTo(this._markers[this._markers.length-2].getLatLng())},_cleanUpShape:function(){this._markers.length>1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:3>this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.cont:(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area;return t?L.GeometryUtil.readableArea(t,this.options.metric):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexAdded:function(){if(!this.options.allowIntersection&&this.options.showArea){var t=this._poly.getLatLngs();this._area=L.GeometryUtil.geodesicArea(t)}},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent({text:this._endLabelText}),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){this._shape?this._shape.setRadius(this._startLatLng.distanceTo(t)):(this._shape=new L.Circle(this._startLatLng,this._startLatLng.distanceTo(t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=(this.options.metric,this.options.showRadius),a=this.options.metric;this._tooltip.updatePosition(i),this._isDrawing&&(this._drawShape(i),e=this._shape.getRadius().toFixed(1),this._tooltip.updateContent({text:this._endLabelText,subtext:o?"Radius: "+L.GeometryUtil.readableDistance(e,a):""}))}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:L.drawLocal.draw.handlers.marker.tooltip.start}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._marker&&(this._marker.off("click",this._onClick,this),this._map.off("click",this._onClick,this).removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?this._marker.setLatLng(e):(this._marker=new L.Marker(e,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset}),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_fireCreatedEvent:function(){var t=new L.Marker(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({options:{icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"})},initialize:function(t,e){this._poly=t,L.setOptions(this,e)},addHooks:function(){this._poly._map&&(this._markerGroup||this._initMarkers(),this._poly._map.addLayer(this._markerGroup))},removeHooks:function(){this._poly._map&&(this._poly._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers)},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new L.LayerGroup),this._markers=[];var t,e,i,o,a=this._poly._latlngs;for(t=0,i=a.length;i>t;t++)o=this._createMarker(a[t],t),o.on("click",this._onMarkerClick,this),this._markers.push(o);var s,r;for(t=0,e=i-1;i>t;e=t++)(0!==t||L.Polygon&&this._poly instanceof L.Polygon)&&(s=this._markers[e],r=this._markers[t],this._createMiddleMarker(s,r),this._updatePrevNext(s,r))},_createMarker:function(t,e){var i=new L.Marker(t,{draggable:!0,icon:this.options.icon});return i._origLatLng=t,i._index=e,i.on("drag",this._onMarkerDrag,this),i.on("dragend",this._fireEdit,this),this._markerGroup.addLayer(i),i},_removeMarker:function(t){var e=t._index;this._markerGroup.removeLayer(t),this._markers.splice(e,1),this._poly.spliceLatLngs(e,1),this._updateIndexes(e,-1),t.off("drag",this._onMarkerDrag,this).off("dragend",this._fireEdit,this).off("click",this._onMarkerClick,this)},_fireEdit:function(){this._poly.edited=!0,this._poly.fire("edit")},_onMarkerDrag:function(t){var e=t.target;L.extend(e._origLatLng,e._latlng),e._middleLeft&&e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev,e)),e._middleRight&&e._middleRight.setLatLng(this._getMiddleLatLng(e,e._next)),this._poly.redraw()},_onMarkerClick:function(t){var e=L.Polygon&&this._poly instanceof L.Polygon?4:3,i=t.target;e>this._poly._latlngs.length||(this._removeMarker(i),this._updatePrevNext(i._prev,i._next),i._middleLeft&&this._markerGroup.removeLayer(i._middleLeft),i._middleRight&&this._markerGroup.removeLayer(i._middleRight),i._prev&&i._next?this._createMiddleMarker(i._prev,i._next):i._prev?i._next||(i._prev._middleRight=null):i._next._middleLeft=null,this._fireEdit())},_updateIndexes:function(t,e){this._markerGroup.eachLayer(function(i){i._index>t&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,o,a,s=this._getMiddleLatLng(t,e),r=this._createMarker(s);r.setOpacity(.6),t._middleRight=e._middleLeft=r,o=function(){var o=e._index;r._index=o,r.off("click",i,this).on("click",this._onMarkerClick,this),s.lat=r.getLatLng().lat,s.lng=r.getLatLng().lng,this._poly.spliceLatLngs(o,0,s),this._markers.splice(o,0,r),r.setOpacity(1),this._updateIndexes(o,1),e._index++,this._updatePrevNext(t,r),this._updatePrevNext(r,e)},a=function(){r.off("dragstart",o,this),r.off("dragend",a,this),this._createMiddleMarker(t,r),this._createMiddleMarker(r,e)},i=function(){o.call(this),a.call(this),this._fireEdit()},r.on("click",i,this).on("dragstart",o,this).on("dragend",a,this),this._markerGroup.addLayer(r)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),a=i.project(e.getLatLng());return i.unproject(o._add(a)._divideBy(2))}}),L.Polyline.addInitHook(function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))}),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"})},initialize:function(t,e){this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){this._shape._map&&(this._map=this._shape._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup))},removeHooks:function(){if(this._shape._map){this._unbindMarker(this._moveMarker);for(var t=0,e=this._resizeMarkers.length;e>t;t++)this._unbindMarker(this._resizeMarkers[t]);this._resizeMarkers=null,this._map.removeLayer(this._markerGroup),delete this._markerGroup}this._map=null},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new L.LayerGroup),this._createMoveMarker(),this._createResizeMarker()},_createMoveMarker:function(){},_createResizeMarker:function(){},_createMarker:function(t,e){var i=new L.Marker(t,{draggable:!0,icon:e,zIndexOffset:10});return this._bindMarker(i),this._markerGroup.addLayer(i),i},_bindMarker:function(t){t.on("dragstart",this._onMarkerDragStart,this).on("drag",this._onMarkerDrag,this).on("dragend",this._onMarkerDragEnd,this)},_unbindMarker:function(t){t.off("dragstart",this._onMarkerDragStart,this).off("drag",this._onMarkerDrag,this).off("dragend",this._onMarkerDragEnd,this)},_onMarkerDragStart:function(t){var e=t.target;e.setOpacity(0)},_fireEdit:function(){this._shape.edited=!0,this._shape.fire("edit")},_onMarkerDrag:function(t){var e=t.target,i=e.getLatLng();e===this._moveMarker?this._move(i):this._resize(i),this._shape.redraw()},_onMarkerDragEnd:function(t){var e=t.target;e.setOpacity(1),this._fireEdit()},_move:function(){},_resize:function(){}}),L.Edit=L.Edit||{},L.Edit.Rectangle=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getBounds(),e=t.getCenter();this._moveMarker=this._createMarker(e,this.options.moveIcon)},_createResizeMarker:function(){var t=this._getCorners();this._resizeMarkers=[];for(var e=0,i=t.length;i>e;e++)this._resizeMarkers.push(this._createMarker(t[e],this.options.resizeIcon)),this._resizeMarkers[e]._cornerIndex=e},_onMarkerDragStart:function(t){L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this,t);var e=this._getCorners(),i=t.target,o=i._cornerIndex;this._oppositeCorner=e[(o+2)%4],this._toggleCornerMarkers(0,o)},_onMarkerDragEnd:function(t){var e,i,o=t.target;o===this._moveMarker&&(e=this._shape.getBounds(),i=e.getCenter(),o.setLatLng(i)),this._toggleCornerMarkers(1),this._repositionCornerMarkers(),L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this,t)},_move:function(t){for(var e,i=this._shape.getLatLngs(),o=this._shape.getBounds(),a=o.getCenter(),s=[],r=0,n=i.length;n>r;r++)e=[i[r].lat-a.lat,i[r].lng-a.lng],s.push([t.lat+e[0],t.lng+e[1]]);this._shape.setLatLngs(s),this._repositionCornerMarkers()},_resize:function(t){var e;this._shape.setBounds(L.latLngBounds(t,this._oppositeCorner)),e=this._shape.getBounds(),this._moveMarker.setLatLng(e.getCenter())},_getCorners:function(){var t=this._shape.getBounds(),e=t.getNorthWest(),i=t.getNorthEast(),o=t.getSouthEast(),a=t.getSouthWest();return[e,i,o,a]},_toggleCornerMarkers:function(t){for(var e=0,i=this._resizeMarkers.length;i>e;e++)this._resizeMarkers[e].setOpacity(t)},_repositionCornerMarkers:function(){for(var t=this._getCorners(),e=0,i=this._resizeMarkers.length;i>e;e++)this._resizeMarkers[e].setLatLng(t[e])}}),L.Rectangle.addInitHook(function(){L.Edit.Rectangle&&(this.editing=new L.Edit.Rectangle(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Circle=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getLatLng();this._moveMarker=this._createMarker(t,this.options.moveIcon)},_createResizeMarker:function(){var t=this._shape.getLatLng(),e=this._getResizeMarkerPoint(t);this._resizeMarkers=[],this._resizeMarkers.push(this._createMarker(e,this.options.resizeIcon))},_getResizeMarkerPoint:function(t){var e=this._shape._radius*Math.cos(Math.PI/4),i=this._map.project(t);return this._map.unproject([i.x+e,i.y-e])},_move:function(t){var e=this._getResizeMarkerPoint(t);this._resizeMarkers[0].setLatLng(e),this._shape.setLatLng(t)},_resize:function(t){var e=this._moveMarker.getLatLng(),i=e.distanceTo(t);this._shape.setRadius(i)}}),L.Circle.addInitHook(function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;o>i;i++)e.push(this.cloneLatLng(t[i]));return e},cloneLatLng:function(t){return L.latLng(t.lat,t.lng)}},L.GeometryUtil={geodesicArea:function(t){var e,i,o=t.length,a=0,s=L.LatLng.DEG_TO_RAD;if(o>2){for(var r=0;o>r;r++)e=t[r],i=t[(r+1)%o],a+=(i.lng-e.lng)*s*(2+Math.sin(e.lat*s)+Math.sin(i.lat*s));a=6378137*6378137*a/2}return Math.abs(a)},readableArea:function(t,e){var i;return e?i=t>=1e4?(1e-4*t).toFixed(2)+" ha":t.toFixed(2)+" m²":(t*=.836127,i=t>=3097600?(t/3097600).toFixed(2)+" mi²":t>=4840?(t/4840).toFixed(2)+" acres":Math.ceil(t)+" yd²"),i},readableDistance:function(t,e){var i;return e?i=t>1e3?(t/1e3).toFixed(2)+" km":Math.ceil(t)+" m":(t*=1.09361,i=t>1760?(t/1760).toFixed(2)+" miles":Math.ceil(t)+" yd"),i}},L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._originalPoints,a=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=a-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return this._map?this.newPointIntersects(this._map.latLngToLayerPoint(t),e):!1},newPointIntersects:function(t,e){var i=this._originalPoints,o=i?i.length:0,a=i?i[o-1]:null,s=o-2;return this._tooFewPointsForIntersection(1)?!1:this._lineSegmentsIntersectsRange(a,t,s,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._originalPoints,i=e?e.length:0;return i+=t||0,!this._originalPoints||3>=i},_lineSegmentsIntersectsRange:function(t,e,i,o){var a,s,r=this._originalPoints;o=o||0;for(var n=i;n>o;n--)if(a=r[n-1],s=r[n],L.LineUtil.segmentsIntersect(t,e,a,s))return!0;return!1}}),L.Polygon.include({intersects:function(){var t,e,i,o,a,s=this._originalPoints;return this._tooFewPointsForIntersection()?!1:(t=L.Polyline.prototype.intersects.call(this))?!0:(e=s.length,i=s[0],o=s[e-1],a=e-2,this._lineSegmentsIntersectsRange(o,i,a,1))}}),L.Control.Draw=L.Control.extend({options:{position:"topleft",draw:{},edit:!1},initialize:function(t){if("0.5.1">=L.version)throw Error("Leaflet.draw 0.2.0+ requires Leaflet 0.6.0+. Download latest from https://github.com/Leaflet/Leaflet/");L.Control.prototype.initialize.call(this,t);var e,i;this._toolbars={},L.DrawToolbar&&this.options.draw&&(i=new L.DrawToolbar(this.options.draw),e=L.stamp(i),this._toolbars[e]=i,this._toolbars[e].on("enable",this._toolbarEnabled,this)),L.EditToolbar&&this.options.edit&&(i=new L.EditToolbar(this.options.edit),e=L.stamp(i),this._toolbars[e]=i,this._toolbars[e].on("enable",this._toolbarEnabled,this))},onAdd:function(t){var e,i=L.DomUtil.create("div","leaflet-draw"),o=!1,a="leaflet-draw-toolbar-top";for(var s in this._toolbars)this._toolbars.hasOwnProperty(s)&&(e=this._toolbars[s].addToolbar(t),o||(L.DomUtil.hasClass(e,a)||L.DomUtil.addClass(e.childNodes[0],a),o=!0),i.appendChild(e));return i},onRemove:function(){for(var t in this._toolbars)this._toolbars.hasOwnProperty(t)&&this._toolbars[t].removeToolbar()},setDrawingOptions:function(t){for(var e in this._toolbars)this._toolbars[e]instanceof L.DrawToolbar&&this._toolbars[e].setOptions(t)},_toolbarEnabled:function(t){var e=""+L.stamp(t.target);for(var i in this._toolbars)this._toolbars.hasOwnProperty(i)&&i!==e&&this._toolbars[i].disable()}}),L.Map.mergeOptions({drawControlTooltips:!0,drawControl:!1}),L.Map.addInitHook(function(){this.options.drawControl&&(this.drawControl=new L.Control.Draw,this.addControl(this.drawControl))}),L.Toolbar=L.Class.extend({includes:[L.Mixin.Events],initialize:function(t){L.setOptions(this,t),this._modes={},this._actionButtons=[],this._activeMode=null},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},removeToolbar:function(){for(var t in this._modes)this._modes.hasOwnProperty(t)&&(this._disposeButton(this._modes[t].button,this._modes[t].handler.enable),this._modes[t].handler.disable(),this._modes[t].handler.off("enabled",this._handlerActivated,this).off("disabled",this._handlerDeactivated,this));this._modes={};for(var e=0,i=this._actionButtons.length;i>e;e++)this._disposeButton(this._actionButtons[e].button,this._actionButtons[e].callback);this._actionButtons=[],this._actionsContainer=null},_initModeHandler:function(t,e,i,o,a){var s=t.type;this._modes[s]={},this._modes[s].handler=t,this._modes[s].button=this._createButton({title:a,className:o+"-"+s,container:e,callback:this._modes[s].handler.enable,context:this._modes[s].handler}),this._modes[s].buttonIndex=i,this._modes[s].handler.on("enabled",this._handlerActivated,this).on("disabled",this._handlerDeactivated,this)},_createButton:function(t){var e=L.DomUtil.create("a",t.className||"",t.container);return e.href="#",t.text&&(e.innerHTML=t.text),t.title&&(e.title=t.title),L.DomEvent.on(e,"click",L.DomEvent.stopPropagation).on(e,"mousedown",L.DomEvent.stopPropagation).on(e,"dblclick",L.DomEvent.stopPropagation).on(e,"click",L.DomEvent.preventDefault).on(e,"click",t.callback,t.context),e},_disposeButton:function(t,e){L.DomEvent.off(t,"click",L.DomEvent.stopPropagation).off(t,"mousedown",L.DomEvent.stopPropagation).off(t,"dblclick",L.DomEvent.stopPropagation).off(t,"click",L.DomEvent.preventDefault).off(t,"click",e)},_handlerActivated:function(t){this._activeMode&&this._activeMode.handler.enabled()&&this._activeMode.handler.disable(),this._activeMode=this._modes[t.handler],L.DomUtil.addClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._showActionsToolbar(),this.fire("enable")},_handlerDeactivated:function(){this._hideActionsToolbar(),L.DomUtil.removeClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._activeMode=null,this.fire("disable")},_createActions:function(t){for(var e,i,o=L.DomUtil.create("ul","leaflet-draw-actions"),a=t.length,s=0;a>s;s++)e=L.DomUtil.create("li","",o),i=this._createButton({title:t[s].title,text:t[s].text,container:e,callback:t[s].callback,context:t[s].context}),this._actionButtons.push({button:i,callback:t[s].callback});return o},_showActionsToolbar:function(){var t=this._activeMode.buttonIndex,e=this._lastButtonIndex,i=26,o=1,a=t*i+t*o-1;this._actionsContainer.style.top=a+"px",0===t&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-top")),t===e&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-bottom")),this._actionsContainer.style.display="block"},_hideActionsToolbar:function(){this._actionsContainer.style.display="none",L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-top"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-bottom")}}),L.Tooltip=L.Class.extend({initialize:function(t){this._map=t,this._popupPane=t._panes.popupPane,this._container=t.options.drawControlTooltips?L.DomUtil.create("div","leaflet-draw-tooltip",this._popupPane):null,this._singleLineLabel=!1},dispose:function(){this._container&&(this._popupPane.removeChild(this._container),this._container=null)},updateContent:function(t){return this._container?(t.subtext=t.subtext||"",0!==t.subtext.length||this._singleLineLabel?t.subtext.length>0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+""+"
                            ":"")+""+t.text+"",this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(i.style.visibility="inherit",L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this}}),L.DrawToolbar=L.Toolbar.extend({options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e])); L.Toolbar.prototype.initialize.call(this,t)},addToolbar:function(t){var e=L.DomUtil.create("div","leaflet-draw-section"),i=0,o="leaflet-draw-draw";return this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this.options.polyline&&this._initModeHandler(new L.Draw.Polyline(t,this.options.polyline),this._toolbarContainer,i++,o,L.drawLocal.draw.toolbar.buttons.polyline),this.options.polygon&&this._initModeHandler(new L.Draw.Polygon(t,this.options.polygon),this._toolbarContainer,i++,o,L.drawLocal.draw.toolbar.buttons.polygon),this.options.rectangle&&this._initModeHandler(new L.Draw.Rectangle(t,this.options.rectangle),this._toolbarContainer,i++,o,L.drawLocal.draw.toolbar.buttons.rectangle),this.options.circle&&this._initModeHandler(new L.Draw.Circle(t,this.options.circle),this._toolbarContainer,i++,o,L.drawLocal.draw.toolbar.buttons.circle),this.options.marker&&this._initModeHandler(new L.Draw.Marker(t,this.options.marker),this._toolbarContainer,i++,o,L.drawLocal.draw.toolbar.buttons.marker),this._lastButtonIndex=--i,this._actionsContainer=this._createActions([{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]),e.appendChild(this._toolbarContainer),e.appendChild(this._actionsContainer),e},setOptions:function(t){L.setOptions(this,t);for(var e in this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({options:{edit:{selectedPathOptions:{color:"#fe57a1",opacity:.6,dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1}},remove:{},featureGroup:null},initialize:function(t){t.edit&&(t.edit.selectedPathOptions===undefined&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit=L.extend({},this.options.edit,t.edit)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},addToolbar:function(t){var e=L.DomUtil.create("div","leaflet-draw-section"),i=0,o="leaflet-draw-edit",a=this.options.featureGroup;return this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,this.options.edit&&this._initModeHandler(new L.EditToolbar.Edit(t,{featureGroup:a,selectedPathOptions:this.options.edit.selectedPathOptions}),this._toolbarContainer,i++,o,L.drawLocal.edit.toolbar.buttons.edit),this.options.remove&&this._initModeHandler(new L.EditToolbar.Delete(t,{featureGroup:a}),this._toolbarContainer,i++,o,L.drawLocal.edit.toolbar.buttons.remove),this._lastButtonIndex=--i,this._actionsContainer=this._createActions([{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}]),e.appendChild(this._toolbarContainer),e.appendChild(this._actionsContainer),this._checkDisabled(),a.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){L.Toolbar.prototype.removeToolbar.call(this),this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=this.options.featureGroup,i=0!==e.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},includes:L.Mixin.Events,initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),this._selectedPathOptions=e.selectedPathOptions,this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this),this.fire("enabled",{handler:this.type}),this._map.fire("draw:editstart",{handler:this.type}))},disable:function(){this._enabled&&(this.fire("disabled",{handler:this.type}),this._map.fire("draw:editstop",{handler:this.type}),this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._featureGroup.eachLayer(function(t){this._revertLayer(t)},this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer(function(e){e.edited&&(t.addLayer(e),e.edited=!1)}),this._map.fire("draw:edited",{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(this._uneditedLayerProps[e]=t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?{latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?{latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:{latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())})},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):t.setLatLng(this._uneditedLayerProps[e].latlng))},_toggleMarkerHighlight:function(t){if(t._icon){var e=t._icon;e.style.display="none",L.DomUtil.hasClass(e,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(e,"leaflet-edit-marker-selected"),this._offsetMarker(e,-4)):(L.DomUtil.addClass(e,"leaflet-edit-marker-selected"),this._offsetMarker(e,4)),e.style.display=""}},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"},_enableLayerEdit:function(t){var e,i=t.layer||t.target||t,o=i instanceof L.Marker;(!o||i._icon)&&(this._backupLayer(i),this._selectedPathOptions&&(e=L.Util.extend({},this._selectedPathOptions),o?this._toggleMarkerHighlight(i):(i.options.previousOptions=i.options,i instanceof L.Circle||i instanceof L.Polygon||i instanceof L.Rectangle||(e.fill=!1),i.setStyle(e))),o?(i.dragging.enable(),i.on("dragend",this._onMarkerDragEnd)):i.editing.enable())},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this)):e.editing.disable()},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},includes:L.Mixin.Events,initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this),this.fire("enabled",{handler:this.type}),this._map.fire("draw:editstart",{handler:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),this.fire("disabled",{handler:this.type}),this._map.fire("draw:editstop",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.layerGroup,this._tooltip=new L.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer(function(t){this._deletableLayers.addLayer(t)},this)},save:function(){this._map.fire("draw:deleted",{layers:this._deletedLayers})},_enableLayerDelete:function(t){var e=t.layer||t.target||t;e.on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e)},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})})(this,document); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/Leaflet.plugins/arc.js ================================================ var D2R = Math.PI / 180; var R2D = 180 / Math.PI; var Coord = function(lon,lat) { this.lon = lon; this.lat = lat; this.x = D2R * lon; this.y = D2R * lat; }; Coord.prototype.view = function() { return String(this.lon).slice(0, 4) + ',' + String(this.lat).slice(0, 4); }; Coord.prototype.antipode = function() { var anti_lat = -1 * this.lat; if (this.lon < 0) { var anti_lon = 180 + this.lon; } else { var anti_lon = (180 - this.lon) * -1; } return new Coord(anti_lon, anti_lat); }; var LineString = function() { this.coords = []; this.length = 0; }; LineString.prototype.move_to = function(coord) { this.length++; this.coords.push(coord); }; var Arc = function(properties) { this.properties = properties || {}; this.geometries = [] }; Arc.prototype.json = function() { if (this.geometries.length <= 0) { return {'geometry': { 'type': 'LineString', 'coordinates': null }, 'type': 'Feature', 'properties': this.properties }; } else if (this.geometries.length == 1) { return {'geometry': { 'type': 'LineString', 'coordinates': this.geometries[0].coords }, 'type': 'Feature', 'properties': this.properties }; } else { var multiline = [] for (i = 0; i < this.geometries.length; i++) { multiline.push(this.geometries[i].coords); } return {'geometry': { 'type': 'MultiLineString', 'coordinates': multiline }, 'type': 'Feature', 'properties': this.properties }; } }; // TODO - output proper multilinestring Arc.prototype.wkt = function() { var wkt_string = ''; for (i = 0; i < this.geometries.length; i++) { if (this.geometries[i].coords.length === 0) { return 'LINESTRING(empty)'; } else { var wkt = 'LINESTRING('; this.geometries[i].coords.forEach(function(c,idx) { wkt += c[0] + ' ' + c[1] + ','; }); wkt_string += wkt.substring(0, wkt.length - 1) + ')'; } } return wkt_string; }; /* * http://en.wikipedia.org/wiki/Great-circle_distance * */ var GreatCircle = function(start,end,properties) { this.start = start; this.end = end; this.properties = properties || {}; var w = this.start.x - this.end.x; var h = this.start.y - this.end.y; var z = Math.pow(Math.sin(h / 2.0), 2) + Math.cos(this.start.y) * Math.cos(this.end.y) * Math.pow(Math.sin(w / 2.0), 2); this.g = 2.0 * Math.asin(Math.sqrt(z)); if (this.g == Math.PI) { throw new Error('it appears ' + start.view() + ' and ' + end.view() + " are 'antipodal', e.g diametrically opposite, thus there is no single route but rather infinite"); } else if (isNaN(this.g)) { throw new Error('could not calculate great circle between ' + start + ' and ' + end); } }; /* * http://williams.best.vwh.net/avform.htm#Intermediate */ GreatCircle.prototype.interpolate = function(f) { var A = Math.sin((1 - f) * this.g) / Math.sin(this.g); var B = Math.sin(f * this.g) / Math.sin(this.g); var x = A * Math.cos(this.start.y) * Math.cos(this.start.x) + B * Math.cos(this.end.y) * Math.cos(this.end.x); var y = A * Math.cos(this.start.y) * Math.sin(this.start.x) + B * Math.cos(this.end.y) * Math.sin(this.end.x); var z = A * Math.sin(this.start.y) + B * Math.sin(this.end.y); var lat = R2D * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); var lon = R2D * Math.atan2(y, x); return [lon, lat]; }; /* * Generate points along the great circle */ GreatCircle.prototype.Arc = function(npoints,options) { var first_pass = []; //var minx = 0; //var maxx = 0; if (npoints <= 2) { first_pass.push([this.start.lon, this.start.lat]); first_pass.push([this.end.lon, this.end.lat]); } else { var delta = 1.0 / (npoints - 1); for (var i = 0; i < npoints; i++) { var step = delta * i; var pair = this.interpolate(step); //minx = Math.min(minx,pair[0]); //maxx = Math.max(maxx,pair[0]); first_pass.push(pair); } } /* partial port of dateline handling from: gdal/ogr/ogrgeometryfactory.cpp TODO - does not handle all wrapping scenarios yet */ var bHasBigDiff = false; var dfMaxSmallDiffLong = 0; for (var i = 1; i < first_pass.length; i++) { //if (minx > 170 && maxx > 180) { // } var dfPrevX = first_pass[i-1][0]; var dfX = first_pass[i][0]; var dfDiffLong = Math.abs(dfX - dfPrevX); if (dfDiffLong > 350 && ((dfX > 170 && dfPrevX < -170) || (dfPrevX > 170 && dfX < -170))) { bHasBigDiff = true; } else if (dfDiffLong > dfMaxSmallDiffLong) { dfMaxSmallDiffLong = dfDiffLong; } } var poMulti = [] if (bHasBigDiff && dfMaxSmallDiffLong < 10) { var poNewLS = [] poMulti.push(poNewLS); for (var i = 0; i < first_pass.length; i++) { var dfX = parseFloat(first_pass[i][0]); if (i > 0 && Math.abs(dfX - first_pass[i-1][0]) > 350) { var dfX1 = parseFloat(first_pass[i-1][0]); var dfY1 = parseFloat(first_pass[i-1][1]); var dfX2 = parseFloat(first_pass[i][0]); var dfY2 = parseFloat(first_pass[i][1]); if (dfX1 > -180 && dfX1 < -170 && dfX2 == 180 && i+1 < first_pass.length && first_pass[i-1][0] > -180 && first_pass[i-1][0] < -170) { poNewLS.push([-180, first_pass[i][1]]); i++; poNewLS.push([first_pass[i][0], first_pass[i][1]]); continue; } else if (dfX1 > 170 && dfX1 < 180 && dfX2 == -180 && i+1 < first_pass.length && first_pass[i-1][0] > 170 && first_pass[i-1][0] < 180) { poNewLS.push([180, first_pass[i][1]]); i++; poNewLS.push([first_pass[i][0], first_pass[i][1]]); continue; } if (dfX1 < -170 && dfX2 > 170) { // swap dfX1, dfX2 var tmpX = dfX1; dfX1 = dfX2; dfX2 = tmpX; // swap dfY1, dfY2 var tmpY = dfY1; dfY1 = dfY2; dfY2 = tmpY; } if (dfX1 > 170 && dfX2 < -170) { dfX2 += 360; } if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2) { var dfRatio = (180 - dfX1) / (dfX2 - dfX1); var dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1; poNewLS.push([first_pass[i-1][0] > 170 ? 180 : -180, dfY]); poNewLS = []; poNewLS.push([first_pass[i-1][0] > 170 ? -180 : 180, dfY]); poMulti.push(poNewLS); } else { poNewLS = []; poMulti.push(poNewLS); } poNewLS.push([dfX, first_pass[i][1]]); } else { poNewLS.push([first_pass[i][0], first_pass[i][1]]); } } } else { // add normally var poNewLS = [] poMulti.push(poNewLS); for (var i = 0; i < first_pass.length; i++) { poNewLS.push([first_pass[i][0],first_pass[i][1]]); } } var arc = new Arc(this.properties); for (var i = 0; i < poMulti.length; i++) { var line = new LineString(); arc.geometries.push(line); var points = poMulti[i]; for (var j = 0; j < points.length; j++) { line.move_to(points[j]); } } return arc; }; if (typeof window === 'undefined') { // nodejs module.exports.Coord = Coord; module.exports.Arc = Arc; module.exports.GreatCircle = GreatCircle; } else { // browser var arc = {}; arc.Coord = Coord; arc.Arc = Arc; arc.GreatCircle = GreatCircle; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/README.md ================================================ # arc.js Calculate great circles routes. Algorithms from http://williams.best.vwh.net/avform.htm#Intermediate # Installation For NodeJS usage, install with npm: npm install -g # Usage The idea is you may have one or many start and end points. Create Coordinate pairs from the longitude (x) and latitude (y) values of each place and pass these (and optionally a properties object), to the GreatCircle constructor: var arc = require('arc'); var start = new arc.Coord(-122, 48); var end = new arc.Coord(-77, 39); var gc = new arc.GreatCircle(start, end, {'name': 'Seattle to DC'}); var line = gc.Arc(6); Then `line` will be a raw sequence of the start and end coordinates plus an arc of intermediate coordinate pairs. > line { properties: { name: 'Seattle to DC' }, coords: [ [ -122, 48.00000000000001 ], [ -112.06161978373486, 47.7241672604096 ], [ -102.38404317022653, 46.60813199882492 ], [ -93.22718895342909, 44.716217302635705 ], [ -84.74823988299501, 42.14415510795357 ], [ -77, 38.99999999999999 ] ], length: 6 } You can then serialize to a GeoJSON geometry format: > line.json(); { geometry: { type: 'LineString', coordinates: [ [Object], [Object], [Object], [Object], [Object], [Object] ] }, type: 'Feature', properties: { name: 'Seattle to DC' } } Or to WKT (Well known text): > line.wkt(); 'LINESTRING(-122 48.00000000000001,-112.06161978373486 47.7241672604096,-102.38404317022653 46.60813199882492,-93.22718895342909 44.716217302635705,-84.74823988299501 42.14415510795357,-77 38.99999999999999)' It is then up to you to add up these features to create fully fledged geodata. See the examples/ directory for sample code to create a GeoJSON file from multiple routes. ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/arc.js ================================================ var D2R = Math.PI / 180; var R2D = 180 / Math.PI; var Coord = function(lon,lat) { this.lon = lon; this.lat = lat; this.x = D2R * lon; this.y = D2R * lat; }; Coord.prototype.view = function() { return String(this.lon).slice(0, 4) + ',' + String(this.lat).slice(0, 4); }; Coord.prototype.antipode = function() { var anti_lat = -1 * this.lat; if (this.lon < 0) { var anti_lon = 180 + this.lon; } else { var anti_lon = (180 - this.lon) * -1; } return new Coord(anti_lon, anti_lat); }; var LineString = function() { this.coords = []; this.length = 0; }; LineString.prototype.move_to = function(coord) { this.length++; this.coords.push(coord); }; var Arc = function(properties) { this.properties = properties || {}; this.geometries = [] }; Arc.prototype.json = function() { if (this.geometries.length <= 0) { return {'geometry': { 'type': 'LineString', 'coordinates': null }, 'type': 'Feature', 'properties': this.properties }; } else if (this.geometries.length == 1) { return {'geometry': { 'type': 'LineString', 'coordinates': this.geometries[0].coords }, 'type': 'Feature', 'properties': this.properties }; } else { var multiline = [] for (i = 0; i < this.geometries.length; i++) { multiline.push(this.geometries[i].coords); } return {'geometry': { 'type': 'MultiLineString', 'coordinates': multiline }, 'type': 'Feature', 'properties': this.properties }; } }; // TODO - output proper multilinestring Arc.prototype.wkt = function() { var wkt_string = ''; for (i = 0; i < this.geometries.length; i++) { if (this.geometries[i].coords.length === 0) { return 'LINESTRING(empty)'; } else { var wkt = 'LINESTRING('; this.geometries[i].coords.forEach(function(c,idx) { wkt += c[0] + ' ' + c[1] + ','; }); wkt_string += wkt.substring(0, wkt.length - 1) + ')'; } } return wkt_string; }; /* * http://en.wikipedia.org/wiki/Great-circle_distance * */ var GreatCircle = function(start,end,properties) { this.start = start; this.end = end; this.properties = properties || {}; var w = this.start.x - this.end.x; var h = this.start.y - this.end.y; var z = Math.pow(Math.sin(h / 2.0), 2) + Math.cos(this.start.y) * Math.cos(this.end.y) * Math.pow(Math.sin(w / 2.0), 2); this.g = 2.0 * Math.asin(Math.sqrt(z)); if (this.g == Math.PI) { throw new Error('it appears ' + start.view() + ' and ' + end.view() + " are 'antipodal', e.g diametrically opposite, thus there is no single route but rather infinite"); } else if (isNaN(this.g)) { throw new Error('could not calculate great circle between ' + start + ' and ' + end); } }; /* * http://williams.best.vwh.net/avform.htm#Intermediate */ GreatCircle.prototype.interpolate = function(f) { var A = Math.sin((1 - f) * this.g) / Math.sin(this.g); var B = Math.sin(f * this.g) / Math.sin(this.g); var x = A * Math.cos(this.start.y) * Math.cos(this.start.x) + B * Math.cos(this.end.y) * Math.cos(this.end.x); var y = A * Math.cos(this.start.y) * Math.sin(this.start.x) + B * Math.cos(this.end.y) * Math.sin(this.end.x); var z = A * Math.sin(this.start.y) + B * Math.sin(this.end.y); var lat = R2D * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); var lon = R2D * Math.atan2(y, x); return [lon, lat]; }; /* * Generate points along the great circle */ GreatCircle.prototype.Arc = function(npoints,options) { var first_pass = []; //var minx = 0; //var maxx = 0; if (npoints <= 2) { first_pass.push([this.start.lon, this.start.lat]); first_pass.push([this.end.lon, this.end.lat]); } else { var delta = 1.0 / (npoints - 1); for (var i = 0; i < npoints; i++) { var step = delta * i; var pair = this.interpolate(step); //minx = Math.min(minx,pair[0]); //maxx = Math.max(maxx,pair[0]); first_pass.push(pair); } } /* partial port of dateline handling from: gdal/ogr/ogrgeometryfactory.cpp TODO - does not handle all wrapping scenarios yet */ var bHasBigDiff = false; var dfMaxSmallDiffLong = 0; for (var i = 1; i < first_pass.length; i++) { //if (minx > 170 && maxx > 180) { // } var dfPrevX = first_pass[i-1][0]; var dfX = first_pass[i][0]; var dfDiffLong = Math.abs(dfX - dfPrevX); if (dfDiffLong > 350 && ((dfX > 170 && dfPrevX < -170) || (dfPrevX > 170 && dfX < -170))) { bHasBigDiff = true; } else if (dfDiffLong > dfMaxSmallDiffLong) { dfMaxSmallDiffLong = dfDiffLong; } } var poMulti = [] if (bHasBigDiff && dfMaxSmallDiffLong < 10) { var poNewLS = [] poMulti.push(poNewLS); for (var i = 0; i < first_pass.length; i++) { var dfX = parseFloat(first_pass[i][0]); if (i > 0 && Math.abs(dfX - first_pass[i-1][0]) > 350) { var dfX1 = parseFloat(first_pass[i-1][0]); var dfY1 = parseFloat(first_pass[i-1][1]); var dfX2 = parseFloat(first_pass[i][0]); var dfY2 = parseFloat(first_pass[i][1]); if (dfX1 > -180 && dfX1 < -170 && dfX2 == 180 && i+1 < first_pass.length && first_pass[i-1][0] > -180 && first_pass[i-1][0] < -170) { poNewLS.push([-180, first_pass[i][1]]); i++; poNewLS.push([first_pass[i][0], first_pass[i][1]]); continue; } else if (dfX1 > 170 && dfX1 < 180 && dfX2 == -180 && i+1 < first_pass.length && first_pass[i-1][0] > 170 && first_pass[i-1][0] < 180) { poNewLS.push([180, first_pass[i][1]]); i++; poNewLS.push([first_pass[i][0], first_pass[i][1]]); continue; } if (dfX1 < -170 && dfX2 > 170) { // swap dfX1, dfX2 var tmpX = dfX1; dfX1 = dfX2; dfX2 = tmpX; // swap dfY1, dfY2 var tmpY = dfY1; dfY1 = dfY2; dfY2 = tmpY; } if (dfX1 > 170 && dfX2 < -170) { dfX2 += 360; } if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2) { var dfRatio = (180 - dfX1) / (dfX2 - dfX1); var dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1; poNewLS.push([first_pass[i-1][0] > 170 ? 180 : -180, dfY]); poNewLS = []; poNewLS.push([first_pass[i-1][0] > 170 ? -180 : 180, dfY]); poMulti.push(poNewLS); } else { poNewLS = []; poMulti.push(poNewLS); } poNewLS.push([dfX, first_pass[i][1]]); } else { poNewLS.push([first_pass[i][0], first_pass[i][1]]); } } } else { // add normally var poNewLS = []; poMulti.push(poNewLS); for (var i = 0; i < first_pass.length; i++) { poNewLS.push([first_pass[i][0],first_pass[i][1]]); } } var arc = new Arc(this.properties); for (var i = 0; i < poMulti.length; i++) { var line = new LineString(); arc.geometries.push(line); var points = poMulti[i]; for (var j = 0; j < points.length; j++) { line.move_to(points[j]); } } return arc; }; if (typeof window === 'undefined') { // nodejs module.exports.Coord = Coord; module.exports.Arc = Arc; module.exports.GreatCircle = GreatCircle; } else { // browser var arc = {}; arc.Coord = Coord; arc.Arc = Arc; arc.GreatCircle = GreatCircle; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/bezier.geojson ================================================ { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ 77.032660, 38.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 50.913245 ], [ 0, 20.913245 ] ] } }, { "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 50.913245 ], [ 0, 20.913245 ] ] } } ] } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/bezier.html ================================================ Draw Great Circle Arcs with Leaflet

                            source code on github

                            spread - the amount that control points are spread around origins
                            rotate - the amount each exit point rotates around the origin
                            input
                            output
                            ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/bezier.js ================================================ function bezier(pts) { function curve(points) { var c = []; var steps = 40; for (var i = 0; i <= steps; i++) { var t = i / steps; var pt = [ Math.pow(1 - t, 3) * points[0][0] + 3 * t * Math.pow(1 - t, 2) * points[1][0] + 3 * (1 - t) * Math.pow(t, 2) * points[2][0] + Math.pow(t, 3) * points[3][0], Math.pow(1 - t, 3) * points[0][1] + 3 * t * Math.pow(1-t,2) * points[1][1] + 3 * (1-t) * Math.pow(t,2) * points[2][1] + Math.pow(t, 3) * points[3][1] ]; c.push(pt); } return c; } var c = []; if (pts.length < 4) return pts; for (var i = 0; i < pts.length; i += 3) { if (i + 4 <= pts.length) { c = c.concat(curve(pts.slice(i, i + 4))); } } return c; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/bezier.py ================================================ from PIL import Image import math # http://stackoverflow.com/questions/3515909/question-about-the-implementation-of-bezier-curves # draws a single point on our image def drawPoint( img, loc, size=5, color=(0,0,0) ): px = img.load() for x in range(size): for y in range(size): xloc = loc[0] + x - size/2 yloc = loc[1] + y - size/2 px[ xloc, yloc ] = color # draws a simple bezier curve with 4 points def drawCurve( img, points ): steps = 20 for i in range(steps): t = i / float(steps) xloc = math.pow(1-t,3) * points[0][0] \ + 3*t*math.pow(1-t,2) * points[1][0] \ + 3*(1-t)*math.pow(t,2) * points[2][0] \ + math.pow(t,3) * points[3][0] yloc = math.pow(1-t,3) * points[0][1] \ + 3*t*math.pow(1-t,2) * points[1][1] \ + 3*(1-t)*math.pow(t,2) * points[2][1] \ + math.pow(t,3) * points[3][1] drawPoint( img, (xloc,yloc), size=2 ) # draws a bezier curve with any number of points def drawBezier( img, points ): for i in range(0,len(points),3): if( i+4 < len(points) ): drawCurve( img, points[i:i+4] ) # draws a smooth bezier curve by adding points that # force smoothness def drawSmoothBezier( img, points ): newpoints = [] for i in range(len(points)): # add the next point (and draw it) newpoints.append(points[i]) drawPoint( img, points[i], color=(255,0,0) ) if( i % 2 == 0 and i>0 and i+1 tolerance) { var gc = new arc.GreatCircle( new arc.Coord(a[0], a[1]), new arc.Coord(b[0], b[1])); var line = gc.Arc(10); geojson.features[i].geometry.coordinates[j].arc = line.coords; } } var co = []; for (var k = geojson.features[i].geometry.coordinates.length - 1; k >= 0; k--) { if (geojson.features[i].geometry.coordinates[k].arc) { co = geojson.features[i].geometry.coordinates[k].arc.concat(co); } else { co.unshift(geojson.features[i].geometry.coordinates[k]); } } geojson.features[i].geometry.coordinates = co; } } fs.writeFileSync('tracks_round.geojson', JSON.stringify(geojson)); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/example/routes.csv ================================================ lon1, lat1, lon2, lat2, name, other attribute -122, 48, -77, 39, "Seattle to DC", 1.0 -122, 48, 0, 51, "Seattle to London", 2 -77, 39, 0, 51, "DC to London", 3.1 ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/example/tracks.geojson ================================================ { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "TrackMe export", "cmt": "", "desc": "", "src": "", "link1_href": "", "link1_text": "", "link1_type": "", "link2_href": "", "link2_text": "", "link2_type": "", "number": 0, "type": "" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.032660, 38.913245 ], [ -77.034612, 38.918602 ], [ -77.032599, 38.913949 ], [ -77.018279, 38.898226 ], [ -77.040944, 38.862367 ], [ -77.050392, 38.854440 ], [ -77.054272, 38.838073 ], [ -77.017570, 38.833381 ], [ -77.053437, 38.827721 ], [ -77.050250, 38.847827 ], [ -77.054272, 38.838073 ], [ -77.033720, 38.890679 ], [ -77.031259, 38.902575 ], [ -77.029469, 38.918511 ], [ -77.030980, 38.924843 ], [ -77.029266, 38.913984 ], [ -77.034128, 38.903978 ], [ -77.044539, 38.903735 ], [ -77.029266, 38.913984 ], [ -77.029234, 38.925337 ], [ -77.012885, 38.916419 ], [ -77.028699, 38.924231 ], [ -77.027471, 38.914844 ], [ -77.034128, 38.903978 ], [ -77.034522, 38.917940 ], [ -77.026324, 38.900964 ], [ -77.029561, 38.917554 ], [ -77.028921, 38.929646 ], [ -77.029367, 38.913733 ], [ -77.014371, 38.911169 ], [ -77.018512, 38.915192 ], [ -77.021939, 38.911565 ], [ -77.011958, 38.910804 ], [ -77.013915, 38.906295 ], [ -77.013470, 38.899731 ], [ -77.014351, 38.885039 ], [ -77.046214, 38.867153 ], [ -77.052083, 38.866712 ], [ -77.078684, 38.848110 ], [ -77.083428, 38.843929 ], [ -77.089188, 38.844738 ], [ -77.085842, 38.841012 ], [ -77.089981, 38.844297 ], [ -77.089489, 38.844242 ], [ -77.072871, 38.862038 ], [ -77.051865, 38.868731 ], [ -77.033901, 38.888678 ], [ -77.031197, 38.901439 ], [ -77.040243, 38.915455 ], [ -77.027706, 38.914845 ], [ -77.028410, 38.925087 ], [ -77.033480, 38.935664 ], [ -77.029372, 38.926715 ], [ -77.028996, 38.914429 ], [ -77.028597, 38.929520 ], [ -77.034648, 38.917760 ], [ -77.028298, 38.925456 ], [ -77.034648, 38.917760 ], [ -77.040607, 38.927325 ], [ -77.029241, 38.925560 ], [ -77.026484, 38.910549 ], [ -77.034522, 38.917940 ], [ -77.026296, 38.907455 ], [ -77.030006, 38.919196 ], [ -77.029249, 38.929425 ], [ -77.034596, 38.921222 ], [ -77.025943, 38.907349 ], [ -77.015375, 38.915107 ], [ -77.030643, 38.908942 ], [ -77.034425, 38.921060 ], [ -77.014333, 38.914943 ], [ -77.028358, 38.926457 ], [ -77.008070, 38.923904 ], [ -77.028642, 38.926862 ], [ -77.012633, 38.916032 ], [ -77.024911, 38.922549 ], [ -77.013671, 38.915555 ], [ -77.028642, 38.926862 ], [ -77.015375, 38.915107 ], [ -77.030643, 38.908942 ], [ -77.014333, 38.914943 ], [ -77.028412, 38.925796 ], [ -77.030643, 38.908942 ], [ -77.014333, 38.914943 ], [ -77.028358, 38.926457 ], [ -77.013671, 38.915555 ], [ -77.028642, 38.926862 ], [ -77.012633, 38.916032 ], [ -77.028358, 38.926457 ], [ -77.013671, 38.915555 ], [ -77.028642, 38.926862 ], [ -77.013448, 38.919314 ], [ -77.028731, 38.925438 ], [ -77.012633, 38.916032 ], [ -77.030643, 38.908942 ], [ -77.016478, 38.918230 ], [ -77.028412, 38.925796 ], [ -77.014333, 38.914943 ], [ -77.028412, 38.925796 ], [ -77.012982, 38.915848 ], [ -77.027092, 38.912737 ], [ -77.025964, 38.922705 ], [ -77.026520, 38.910412 ], [ -77.028412, 38.925796 ], [ -77.027092, 38.912737 ], [ -77.014667, 38.911136 ], [ -77.029825, 38.919180 ], [ -77.029550, 38.929886 ], [ -77.028511, 38.924930 ], [ -77.025784, 38.908988 ], [ -77.022533, 38.897128 ], [ -77.027092, 38.912737 ], [ -77.028845, 38.924642 ], [ -77.036027, 38.915000 ], [ -77.029570, 38.925606 ], [ -77.027736, 38.915122 ], [ -77.029570, 38.925606 ], [ -77.030188, 38.914173 ], [ -77.018018, 38.904988 ], [ -77.033472, 38.912425 ], [ -77.029570, 38.925606 ], [ -77.027456, 38.915566 ], [ -77.028217, 38.925163 ], [ -77.027456, 38.915566 ], [ -77.028655, 38.924598 ], [ -77.027591, 38.912803 ], [ -77.034118, 38.921218 ], [ -77.023386, 38.927075 ], [ -77.028539, 38.918085 ], [ -77.029835, 38.929526 ], [ -77.029827, 38.918881 ], [ -77.028468, 38.924870 ], [ -77.027456, 38.915566 ], [ -77.027456, 38.915566 ], [ -77.028494, 38.925089 ], [ -77.036825, 38.912627 ], [ -77.025114, 38.923437 ], [ -77.028267, 38.909684 ], [ -77.027165, 38.902324 ], [ -77.031934, 38.896627 ], [ -77.039813, 38.903209 ], [ -77.045632, 38.903901 ], [ -77.030568, 38.916315 ], [ -77.012601, 38.911333 ], [ -76.993892, 38.901037 ], [ -77.014761, 38.916255 ], [ -77.034345, 38.924869 ], [ -77.026995, 38.906982 ], [ -77.034477, 38.919692 ], [ -77.030568, 38.906860 ], [ -77.033799, 38.919577 ], [ -77.032001, 38.903800 ], [ -77.052535, 38.900782 ], [ -77.076978, 38.897903 ], [ -77.097814, 38.897177 ], [ -77.128704, 38.878212 ], [ -77.145900, 38.880647 ], [ -77.168161, 38.897200 ], [ -77.183484, 38.915130 ], [ -77.197271, 38.934459 ], [ -77.210561, 38.926904 ], [ -77.239636, 38.936190 ], [ -77.298079, 38.947167 ], [ -77.364831, 38.958184 ], [ -77.442106, 38.956276 ], [ -77.485164, 38.988903 ], [ -77.442106, 38.956276 ], [ -77.449138, 38.942914 ], [ -77.449640, 38.905389 ], [ -77.441774, 38.943653 ], [ -77.440313, 38.957837 ], [ -104.769154, 39.904540 ], [ -104.685341, 39.859218 ], [ -104.671741, 39.850356 ], [ -104.609001, 39.766203 ], [ -104.671272, 39.850093 ], [ -104.760958, 39.835503 ], [ -104.792237, 39.780302 ], [ -104.932759, 39.768306 ], [ -104.941705, 39.727885 ], [ -104.942658, 39.743819 ], [ -104.958363, 39.734873 ], [ -104.978095, 39.737937 ], [ -104.987340, 39.734172 ], [ -104.993514, 39.719104 ], [ -104.986151, 39.736755 ], [ -104.996785, 39.739945 ], [ -105.009601, 39.752989 ], [ -104.995515, 39.745836 ], [ -105.009601, 39.752989 ], [ -105.002575, 39.745082 ], [ -104.998169, 39.753824 ], [ -105.002082, 39.749351 ], [ -104.995672, 39.744484 ], [ -104.990929, 39.739726 ], [ -104.987102, 39.733509 ], [ -104.988016, 39.740059 ], [ -105.005996, 39.745252 ], [ -105.001051, 39.748447 ], [ -104.987803, 39.737460 ], [ -104.991562, 39.733843 ], [ -104.995903, 39.720161 ], [ -104.986151, 39.732176 ], [ -105.002434, 39.735160 ], [ -104.995903, 39.720161 ], [ -104.988191, 39.733918 ], [ -104.989714, 39.739132 ], [ -104.991287, 39.744475 ], [ -104.993961, 39.748940 ], [ -104.965729, 39.737157 ], [ -104.987806, 39.736104 ], [ -104.990152, 39.731857 ], [ -104.990795, 39.745118 ], [ -105.000344, 39.752323 ], [ -104.994067, 39.748316 ], [ -104.982776, 39.744695 ], [ -104.987768, 39.737323 ], [ -104.988623, 39.742702 ], [ -104.996473, 39.748392 ], [ -105.003293, 39.748520 ], [ -105.003914, 39.762553 ], [ -105.006119, 39.745118 ], [ -104.998268, 39.751083 ], [ -104.990932, 39.746291 ], [ -104.987581, 39.741675 ], [ -104.986977, 39.734177 ], [ -104.989855, 39.741067 ], [ -104.967807, 39.737685 ], [ -104.949958, 39.735145 ], [ -104.959348, 39.734572 ], [ -104.959462, 39.739871 ], [ -104.958357, 39.734823 ], [ -104.980270, 39.736428 ], [ -104.953181, 39.736819 ], [ -104.974336, 39.733552 ], [ -104.990096, 39.737661 ], [ -105.003914, 39.762553 ], [ -105.046026, 39.848132 ], [ -105.258707, 40.083275 ], [ -105.195809, 40.177767 ], [ -105.122630, 40.172639 ], [ -105.288283, 40.230063 ], [ -105.492617, 40.368270 ], [ -105.548160, 40.339277 ], [ -105.520572, 40.341207 ], [ -105.605344, 40.317352 ], [ -105.521753, 40.341375 ], [ -105.542122, 40.377886 ], [ -105.271894, 40.224166 ], [ -105.258246, 40.105936 ], [ -105.158206, 40.185631 ], [ -105.266621, 40.078443 ], [ -105.023463, 39.824034 ], [ -105.023937, 39.840472 ], [ -104.930527, 39.766908 ], [ -104.953745, 39.735932 ], [ -104.972745, 39.736486 ], [ -104.997802, 39.750880 ], [ -104.992440, 39.767994 ], [ -104.984526, 39.788738 ], [ -104.988695, 39.792524 ], [ -104.981368, 39.812379 ], [ -105.045386, 39.849688 ], [ -105.062522, 39.908723 ], [ -105.078209, 39.897386 ], [ -105.096934, 39.919160 ], [ -105.127032, 39.937561 ], [ -105.179077, 39.954629 ], [ -105.313518, 40.016823 ], [ -105.261057, 40.011129 ], [ -105.263062, 39.999529 ], [ -105.271977, 40.005675 ], [ -105.284913, 40.018266 ], [ -105.279323, 40.015136 ], [ -105.274778, 40.018358 ], [ -105.268053, 40.020249 ], [ -105.275364, 40.018884 ], [ -105.265450, 40.017619 ], [ -105.257585, 40.017621 ], [ -105.264371, 40.017379 ], [ -105.277925, 40.005764 ], [ -105.274095, 40.024061 ], [ -105.263413, 40.016754 ], [ -105.265287, 40.021262 ], [ -105.282444, 40.029186 ], [ -105.269476, 40.010921 ], [ -105.277679, 40.026178 ], [ -105.273473, 40.019368 ], [ -105.280015, 40.018010 ], [ -105.274341, 40.019220 ], [ -105.265258, 40.021343 ], [ -105.259471, 40.022289 ], [ -105.253204, 40.023806 ], [ -105.251546, 40.029169 ], [ -105.244821, 40.031767 ], [ -105.263375, 40.000814 ], [ -105.244693, 40.037455 ], [ -105.245579, 40.032030 ], [ -105.255646, 40.017750 ], [ -105.237622, 40.036159 ], [ -105.248628, 40.024178 ], [ -105.198752, 40.041813 ], [ -105.188454, 40.014305 ], [ -105.224005, 40.050517 ], [ -105.125822, 40.046400 ], [ -105.156175, 40.001903 ], [ -105.126295, 40.003149 ], [ -105.159465, 39.933660 ], [ -105.127615, 39.959034 ], [ -105.103774, 39.922874 ], [ -105.043217, 39.822580 ], [ -105.023720, 39.841660 ], [ -104.985179, 39.830321 ], [ -104.971543, 39.827532 ], [ -104.948702, 39.822560 ], [ -104.903744, 39.789220 ], [ -104.786145, 39.766407 ], [ -104.773194, 39.774471 ], [ -104.801121, 39.758773 ], [ -104.793651, 39.782413 ], [ -104.772920, 39.799323 ], [ -104.673483, 39.848773 ], [ -104.660480, 39.854556 ], [ -77.474336, 38.987056 ], [ -77.442167, 38.943002 ], [ -77.437938, 38.956494 ], [ -77.410992, 38.952815 ], [ -77.345315, 38.948421 ], [ -77.280054, 38.942087 ], [ -77.190585, 38.909422 ], [ -77.145453, 38.881513 ], [ -77.077161, 38.898950 ], [ -77.048000, 38.906860 ], [ -77.028280, 38.924887 ], [ -77.034225, 38.912007 ], [ -77.030035, 38.924887 ], [ -77.035032, 38.919939 ], [ -77.028297, 38.933089 ], [ -77.028475, 38.924926 ], [ -77.039551, 38.906860 ], [ -77.030568, 38.917598 ], [ -77.028473, 38.925032 ], [ -77.039933, 38.912780 ], [ -77.021585, 38.897847 ], [ -77.039551, 38.899649 ], [ -77.039006, 38.915855 ], [ -77.030250, 38.930712 ], [ -77.028401, 38.925090 ], [ -77.034591, 38.911883 ], [ -77.028076, 38.924537 ], [ -77.030843, 38.910437 ], [ -77.034820, 38.923173 ], [ -77.028209, 38.910545 ], [ -77.030193, 38.924887 ], [ -77.027480, 38.911537 ], [ -77.034820, 38.923173 ], [ -77.030596, 38.910535 ], [ -77.028092, 38.924998 ], [ -77.027299, 38.910631 ], [ -77.045872, 38.907235 ], [ -77.034013, 38.920259 ], [ -77.028796, 38.924660 ], [ -77.035105, 38.919045 ], [ -77.042489, 38.921796 ], [ -77.029836, 38.930749 ], [ -77.029663, 38.913655 ], [ -77.033820, 38.934527 ], [ -77.021888, 38.948933 ], [ -77.021528, 38.933596 ], [ -77.021888, 38.948933 ], [ -77.018960, 38.945021 ], [ -77.032853, 38.934747 ], [ -77.028438, 38.925068 ], [ -77.027698, 38.911447 ], [ -77.028329, 38.924740 ], [ -77.033933, 38.911429 ], [ -77.028329, 38.924740 ], [ -77.033933, 38.911429 ], [ -77.026182, 38.901672 ], [ -77.020110, 38.913904 ], [ -77.029953, 38.924869 ], [ -77.035128, 38.912497 ], [ -77.027847, 38.925517 ], [ -77.028013, 38.925117 ], [ -77.033603, 38.911923 ], [ -77.029585, 38.925016 ], [ -77.026216, 38.919809 ], [ -77.022480, 38.916367 ], [ -77.017307, 38.914251 ], [ -77.006042, 38.909357 ], [ -77.000301, 38.910252 ], [ -76.907009, 38.942534 ], [ -76.886837, 38.982528 ], [ -76.867361, 39.002961 ], [ -76.833155, 39.075523 ], [ -76.695361, 39.221924 ], [ -76.597525, 39.240860 ], [ -76.544418, 39.282424 ], [ -76.457712, 39.372244 ], [ -76.269164, 39.474439 ], [ -76.223305, 39.502409 ], [ -76.184533, 39.495725 ], [ -76.188481, 39.521595 ], [ -76.197157, 39.522204 ], [ -76.117258, 39.575296 ], [ -75.804294, 39.641061 ], [ -75.798237, 39.640333 ], [ -75.753082, 39.652346 ], [ -75.712791, 39.662019 ], [ -75.693944, 39.668821 ], [ -75.679860, 39.660098 ], [ -75.662383, 39.676553 ], [ -75.643622, 39.685676 ], [ -75.603951, 39.693915 ], [ -75.602098, 39.700408 ], [ -75.588247, 39.705815 ], [ -75.586744, 39.710269 ], [ -75.582813, 39.713702 ], [ -75.576906, 39.715034 ], [ -75.571104, 39.716061 ], [ -75.565066, 39.716835 ], [ -75.505334, 39.752050 ], [ -75.438660, 39.824228 ], [ -75.348071, 39.863855 ], [ -75.343767, 39.867210 ], [ -75.359243, 39.894532 ], [ -75.361254, 39.920601 ], [ -75.375504, 39.935802 ], [ -75.342367, 39.987913 ], [ -75.354487, 40.014691 ], [ -75.363689, 40.031102 ], [ -75.357955, 40.056203 ], [ -75.332833, 40.054965 ], [ -75.295009, 40.101674 ], [ -75.285445, 40.105032 ], [ -75.297929, 40.156687 ], [ -75.294370, 40.173233 ], [ -75.318788, 40.198906 ], [ -75.372781, 40.278662 ], [ -75.438805, 40.453241 ], [ -75.442051, 40.457339 ], [ -75.445423, 40.461263 ], [ -75.448559, 40.465180 ], [ -75.451835, 40.469206 ], [ -75.464441, 40.485786 ], [ -75.502224, 40.514608 ], [ -75.540567, 40.550436 ], [ -75.533699, 40.550160 ], [ -75.561803, 40.557564 ], [ -75.550267, 40.565689 ], [ -75.567638, 40.592235 ], [ -75.536650, 40.610152 ], [ -75.524847, 40.609977 ], [ -75.518979, 40.610841 ], [ -75.513458, 40.612937 ], [ -75.506873, 40.615327 ], [ -75.478231, 40.621653 ], [ -75.478610, 40.627618 ], [ -75.407468, 40.655794 ], [ -75.398588, 40.655783 ], [ -75.385592, 40.673118 ], [ -75.339602, 40.671157 ], [ -75.331059, 40.680484 ], [ -75.324876, 40.680863 ], [ -75.319013, 40.681799 ], [ -75.267777, 40.684421 ], [ -75.289659, 40.726975 ], [ -75.287690, 40.718001 ], [ -75.311905, 40.797540 ], [ -75.295583, 40.801602 ], [ -75.267486, 40.818646 ], [ -75.291298, 40.845265 ], [ -75.299810, 40.919016 ], [ -75.251553, 40.991911 ], [ -75.234575, 40.985118 ], [ -75.207951, 40.977422 ], [ -75.191369, 40.981831 ], [ -75.174821, 40.989684 ], [ -75.142739, 41.005358 ], [ -75.175258, 40.989729 ], [ -75.142739, 41.005358 ], [ -75.176921, 40.993584 ], [ -75.182809, 40.994453 ], [ -75.188120, 40.990835 ], [ -75.193590, 40.996089 ], [ -75.204734, 41.008388 ], [ -75.236314, 41.041635 ], [ -75.214204, 41.042670 ], [ -75.221975, 41.074316 ], [ -75.237778, 41.088784 ], [ -75.219669, 41.092590 ], [ -75.256662, 41.140973 ], [ -75.188661, 41.091275 ], [ -75.188661, 41.091275 ], [ -75.209854, 41.112772 ], [ -75.188661, 41.091275 ], [ -75.236581, 41.113096 ], [ -75.197029, 41.118017 ], [ -75.193469, 41.089796 ], [ -75.197029, 41.118017 ], [ -75.193469, 41.089796 ], [ -75.263229, 41.148713 ], [ -75.193469, 41.089796 ], [ -75.263229, 41.148713 ], [ -75.237778, 41.088784 ], [ -75.205655, 41.007949 ], [ -75.186293, 40.989236 ], [ -75.207951, 40.977422 ], [ -75.229154, 40.986702 ], [ -75.224784, 40.982193 ], [ -75.173474, 40.989934 ], [ -75.302302, 40.913383 ], [ -75.313709, 40.884837 ], [ -75.291051, 40.852060 ], [ -75.284651, 40.813343 ], [ -75.274322, 40.778826 ], [ -75.254451, 40.734900 ], [ -75.298694, 40.690794 ], [ -75.339602, 40.671157 ], [ -75.407309, 40.655189 ], [ -75.424078, 40.654532 ], [ -75.433781, 40.637891 ], [ -75.455326, 40.634168 ], [ -75.486998, 40.628006 ], [ -75.497280, 40.620773 ], [ -75.553202, 40.606082 ], [ -75.427670, 40.461322 ], [ -75.420325, 40.437959 ], [ -75.365504, 40.309962 ], [ -75.295355, 40.173004 ], [ -75.281000, 40.117424 ], [ -75.357955, 40.056203 ], [ -75.346657, 40.043485 ], [ -75.342367, 39.987913 ], [ -75.335804, 39.972875 ], [ -75.335804, 39.972875 ], [ -75.334662, 39.966021 ], [ -75.334491, 39.958616 ], [ -75.337885, 39.954111 ], [ -75.358817, 39.943566 ], [ -75.361022, 39.918495 ], [ -75.346574, 39.877053 ], [ -75.345619, 39.866813 ], [ -75.371727, 39.858837 ], [ -75.455567, 39.815649 ], [ -75.463927, 39.804583 ], [ -75.482902, 39.792345 ], [ -75.501874, 39.789168 ], [ -75.519190, 39.779592 ], [ -75.535511, 39.765682 ], [ -75.563486, 39.760023 ], [ -75.679077, 39.674228 ], [ -75.694800, 39.668982 ], [ -75.712791, 39.662019 ], [ -75.774331, 39.642253 ], [ -75.871626, 39.646824 ], [ -75.893474, 39.645807 ], [ -75.915097, 39.642379 ], [ -75.978851, 39.626961 ], [ -76.117258, 39.575296 ], [ -76.152646, 39.565456 ], [ -76.194244, 39.518143 ], [ -76.209306, 39.510840 ], [ -76.261071, 39.474748 ], [ -76.251081, 39.484489 ], [ -76.272912, 39.474904 ], [ -76.293712, 39.463703 ], [ -76.312815, 39.455656 ], [ -76.321710, 39.450941 ], [ -76.335329, 39.445102 ], [ -76.355750, 39.442237 ], [ -76.374235, 39.425948 ], [ -76.396664, 39.425171 ], [ -76.402629, 39.421299 ], [ -76.409215, 39.418321 ], [ -76.429879, 39.398835 ], [ -76.453923, 39.374187 ], [ -76.494194, 39.352232 ], [ -76.522967, 39.339841 ], [ -76.530717, 39.308766 ], [ -76.530554, 39.294196 ], [ -76.559920, 39.280244 ], [ -76.642992, 39.248289 ], [ -76.675049, 39.221590 ], [ -76.698642, 39.200064 ], [ -76.720091, 39.176706 ], [ -76.734340, 39.162136 ], [ -76.762448, 39.125668 ], [ -76.797774, 39.108078 ], [ -76.769040, 39.113960 ], [ -76.876989, 38.998047 ], [ -76.904279, 38.972368 ], [ -76.901837, 38.955890 ], [ -76.913992, 38.942647 ], [ -76.931744, 38.923683 ], [ -76.936208, 38.920311 ], [ -76.956594, 38.917657 ], [ -77.017111, 38.918127 ], [ -77.009613, 38.934531 ], [ -77.029490, 38.925016 ], [ -77.032138, 38.913235 ], [ -77.030431, 38.924759 ], [ -77.039037, 38.923319 ], [ -77.034218, 38.912017 ], [ -77.032671, 38.913305 ], [ -77.032016, 38.919214 ], [ -77.028433, 38.924914 ], [ -77.032023, 38.919390 ], [ -77.032075, 38.913090 ], [ -77.031931, 38.918421 ], [ -77.032207, 38.923201 ], [ -77.031720, 38.918373 ], [ -77.032651, 38.913218 ], [ -77.031979, 38.919250 ], [ -77.028520, 38.924877 ], [ -77.031502, 38.930894 ], [ -77.028446, 38.924823 ], [ -77.028446, 38.924823 ], [ -77.031619, 38.930715 ], [ -77.028491, 38.924847 ], [ -77.032668, 38.929654 ], [ -77.028563, 38.924927 ], [ -77.030906, 38.916096 ], [ -77.033318, 38.909610 ], [ -77.032134, 38.914103 ], [ -77.024253, 38.916751 ], [ -77.028459, 38.924854 ], [ -77.032226, 38.918857 ], [ -77.032635, 38.913259 ], [ -77.032030, 38.919458 ], [ -77.028519, 38.924811 ], [ -77.028212, 38.914596 ], [ -77.038377, 38.901825 ], [ -77.081582, 38.896771 ], [ -77.180579, 38.899105 ], [ -77.278811, 38.937363 ], [ -77.387050, 38.956506 ], [ -77.435235, 38.955011 ], [ -77.446711, 38.953254 ], [ -84.431846, 33.622715 ], [ -84.423735, 33.630544 ], [ -84.426919, 33.639945 ], [ -84.440031, 33.641093 ], [ -84.432606, 33.640361 ], [ -84.439992, 33.658076 ], [ -80.960270, 35.207867 ], [ -80.950312, 35.214422 ], [ -80.938343, 35.219275 ], [ -80.953547, 35.220252 ], [ -80.945358, 35.218106 ], [ -80.945551, 35.218160 ], [ -122.420309, 37.764226 ], [ -122.407314, 37.761288 ], [ -122.400963, 37.752364 ], [ -122.421835, 37.760629 ], [ -122.400555, 37.752496 ], [ -122.409406, 37.752856 ], [ -122.415780, 37.752390 ], [ -122.391639, 37.790907 ], [ -122.406460, 37.780187 ], [ -122.411199, 37.775837 ], [ -122.407342, 37.782267 ], [ -122.410238, 37.777235 ], [ -122.424256, 37.758564 ], [ -122.433104, 37.734513 ], [ -122.416838, 37.636672 ], [ -122.391594, 37.617994 ], [ -122.385523, 37.614844 ], [ -84.432407, 33.638066 ], [ -87.900769, 42.950230 ], [ -87.884548, 42.953236 ], [ -87.900713, 42.950241 ], [ -77.041535, 38.851058 ], [ -77.033593, 38.892891 ], [ -77.012796, 38.897818 ], [ -77.032689, 38.928726 ], [ -77.028473, 38.924967 ], [ -77.023470, 38.916834 ], [ -77.017194, 38.916064 ], [ -77.025021, 38.919310 ], [ -77.028481, 38.924886 ], [ -77.038645, 38.921752 ], [ -77.028570, 38.924861 ], [ -77.029627, 38.917986 ], [ -77.028456, 38.924893 ], [ -77.027085, 38.920487 ], [ -77.032637, 38.913261 ], [ -77.029099, 38.917035 ], [ -77.019960, 38.915522 ], [ -77.026674, 38.924317 ], [ -77.031968, 38.921671 ], [ -77.032652, 38.913232 ], [ -77.031876, 38.918587 ], [ -77.028460, 38.924835 ], [ -77.032188, 38.917843 ], [ -77.032650, 38.913245 ], [ -77.031869, 38.917756 ], [ -77.029705, 38.922772 ], [ -77.032706, 38.913259 ], [ -77.031953, 38.918773 ], [ -77.028500, 38.924792 ], [ -77.027879, 38.914692 ], [ -77.029065, 38.924818 ], [ -77.029712, 38.908178 ], [ -77.033774, 38.881386 ], [ -77.045737, 38.856087 ], [ -77.033857, 38.851276 ], [ -71.015872, 42.358913 ], [ -71.030086, 42.365115 ], [ -71.080707, 42.358453 ], [ -71.088773, 42.366029 ], [ -71.092959, 42.374700 ], [ -71.088405, 42.360645 ], [ -71.099311, 42.363205 ], [ -71.093003, 42.358239 ], [ -71.068477, 42.358891 ], [ -71.086297, 42.356759 ], [ -71.071681, 42.359120 ], [ -71.058189, 42.362825 ], [ -71.010260, 42.375009 ], [ -71.016434, 42.365670 ], [ -71.025973, 42.357045 ], [ -71.023141, 42.364674 ], [ -77.036480, 38.852564 ], [ -77.051868, 38.860381 ], [ -77.041998, 38.874187 ], [ -77.029906, 38.895101 ], [ -77.034012, 38.930042 ], [ -77.034645, 38.918375 ], [ -77.032491, 38.913477 ], [ -77.025955, 38.923322 ], [ -77.028933, 38.911223 ], [ -77.034660, 38.919730 ], [ -77.029247, 38.927784 ], [ -77.031517, 38.918380 ], [ -77.031161, 38.908544 ], [ -77.031996, 38.917728 ], [ -77.028577, 38.925126 ], [ -77.022725, 38.916670 ], [ -77.029740, 38.924885 ], [ -77.025220, 38.933910 ], [ -77.029749, 38.924887 ], [ -77.019686, 38.917077 ], [ -77.030067, 38.908150 ], [ -77.024048, 38.916167 ], [ -77.028537, 38.924595 ], [ -77.029175, 38.915129 ], [ -77.031083, 38.905674 ], [ -77.034051, 38.901202 ], [ -77.032057, 38.895051 ], [ -77.030868, 38.905476 ], [ -77.031651, 38.915221 ], [ -77.031514, 38.924366 ], [ -77.033292, 38.935266 ], [ -77.029983, 38.926570 ], [ -77.031932, 38.921922 ], [ -77.032653, 38.913206 ], [ -77.031932, 38.905615 ], [ -77.032673, 38.913266 ], [ -77.031944, 38.917735 ], [ -77.028437, 38.924878 ], [ -77.031907, 38.919784 ], [ -77.032665, 38.913214 ], [ -77.031890, 38.920039 ], [ -77.028482, 38.924851 ], [ -77.032613, 38.913207 ], [ -77.032013, 38.919689 ], [ -77.028469, 38.924911 ], [ -77.033623, 38.907354 ], [ -77.018292, 38.901018 ], [ -77.024467, 38.880321 ], [ -77.129992, 38.953985 ], [ -77.198206, 38.949048 ], [ -77.274077, 38.942604 ], [ -77.341171, 38.948908 ], [ -77.416794, 38.955922 ], [ -77.446848, 38.953343 ], [ -77.451923, 38.945543 ], [ -122.373639, 37.595287 ], [ -122.393216, 37.618463 ], [ -122.476823, 37.695757 ], [ -122.449902, 37.727795 ], [ -122.409045, 37.780740 ], [ -122.317066, 37.806245 ], [ -122.294524, 37.804157 ], [ -122.331458, 37.798654 ], [ -122.404660, 37.789043 ], [ -122.420670, 37.763780 ], [ -122.419953, 37.769898 ], [ -122.416001, 37.761069 ], [ -122.419123, 37.766937 ], [ -122.421796, 37.762586 ], [ -122.412364, 37.760010 ], [ -122.414175, 37.752663 ], [ -122.413513, 37.746875 ], [ -122.415600, 37.739036 ], [ -122.414383, 37.755902 ], [ -122.413318, 37.746475 ], [ -122.413962, 37.751172 ], [ -122.414353, 37.755725 ], [ -122.414942, 37.760353 ], [ -122.429640, 37.761120 ], [ -122.419031, 37.760198 ], [ -122.411511, 37.759192 ], [ -122.425367, 37.759489 ], [ -122.418622, 37.763945 ], [ -122.422347, 37.769672 ], [ -122.419459, 37.774323 ], [ -122.420388, 37.780992 ], [ -122.421395, 37.786297 ], [ -122.422781, 37.792169 ], [ -122.424847, 37.802168 ], [ -122.435035, 37.805947 ], [ -122.454172, 37.804387 ], [ -122.467169, 37.818252 ], [ -122.461657, 37.806668 ], [ -122.467443, 37.804288 ], [ -122.472787, 37.806711 ], [ -122.438611, 37.813337 ], [ -122.431265, 37.804171 ], [ -122.426419, 37.801059 ], [ -122.423788, 37.796877 ], [ -122.422404, 37.789823 ], [ -122.420510, 37.781901 ], [ -122.417714, 37.772951 ], [ -122.415980, 37.761051 ], [ -122.419090, 37.766954 ], [ -122.419358, 37.761828 ], [ -122.419116, 37.766933 ], [ -122.427961, 37.768850 ], [ -122.435581, 37.772254 ], [ -122.453920, 37.771470 ], [ -122.497533, 37.771497 ], [ -122.510697, 37.771413 ], [ -122.493683, 37.694353 ], [ -122.479292, 37.664971 ], [ -122.471367, 37.657230 ], [ -122.457573, 37.637214 ], [ -122.483367, 37.636830 ], [ -122.477525, 37.625445 ], [ -122.487662, 37.625750 ], [ -122.491795, 37.611573 ], [ -122.488027, 37.602901 ], [ -122.507606, 37.576754 ], [ -122.509336, 37.528732 ], [ -122.459271, 37.491158 ], [ -122.438241, 37.475066 ], [ -122.433694, 37.468081 ], [ -122.431114, 37.427699 ], [ -122.406767, 37.390510 ], [ -122.405486, 37.322555 ], [ -122.417721, 37.366648 ], [ -122.404805, 37.325885 ], [ -122.408073, 37.281417 ], [ -122.383151, 37.251870 ], [ -122.397114, 37.285229 ], [ -122.390605, 37.258466 ], [ -122.347660, 37.228061 ], [ -122.356355, 37.184368 ], [ -122.323015, 37.140769 ], [ -122.266279, 37.081490 ], [ -122.222201, 37.039915 ], [ -122.111015, 36.968568 ], [ -122.062186, 36.957241 ], [ -122.048756, 36.960825 ], [ -122.026454, 36.975280 ], [ -122.025236, 36.970345 ], [ -122.011022, 37.046858 ], [ -121.982330, 37.147381 ], [ -121.946279, 37.279446 ], [ -121.900239, 37.320301 ], [ -121.896691, 37.330487 ], [ -121.903673, 37.332472 ], [ -121.937180, 37.353281 ], [ -122.011678, 37.373376 ], [ -122.065693, 37.389828 ], [ -122.107088, 37.407171 ], [ -122.141568, 37.429035 ], [ -122.179558, 37.452599 ], [ -122.208191, 37.471757 ], [ -122.243461, 37.493580 ], [ -122.295042, 37.533563 ], [ -122.317934, 37.570195 ], [ -122.368072, 37.590684 ], [ -122.399924, 37.615838 ], [ -122.404521, 37.656662 ], [ -122.405629, 37.711751 ], [ -122.393724, 37.756658 ], [ -122.393740, 37.762942 ], [ -122.399914, 37.762442 ], [ -122.413515, 37.763723 ], [ -122.425110, 37.764390 ], [ -122.415017, 37.775075 ], [ -122.419052, 37.767127 ], [ -122.419052, 37.767127 ], [ -122.426188, 37.769785 ], [ -122.435237, 37.762734 ], [ -122.439232, 37.757927 ], [ -122.435151, 37.762442 ], [ -122.437062, 37.788509 ], [ -122.454134, 37.818405 ], [ -122.449403, 37.804791 ], [ -122.425093, 37.798471 ], [ -122.435970, 37.799235 ], [ -122.437384, 37.789700 ], [ -122.431999, 37.788055 ], [ -122.430666, 37.781439 ], [ -122.429178, 37.774447 ], [ -122.426787, 37.770258 ], [ -122.419202, 37.766890 ], [ -122.421674, 37.776886 ], [ -122.420087, 37.787131 ], [ -122.416200, 37.775391 ], [ -122.408455, 37.784203 ], [ -122.420700, 37.788208 ], [ -122.420710, 37.782455 ], [ -122.420105, 37.770169 ], [ -122.419693, 37.765014 ], [ -122.419928, 37.769740 ], [ -122.416023, 37.775307 ], [ -122.416213, 37.779859 ], [ -122.422531, 37.772538 ], [ -122.419136, 37.766917 ], [ -122.422223, 37.757241 ], [ -122.418355, 37.752520 ], [ -122.418955, 37.757357 ], [ -122.420789, 37.768441 ], [ -122.420789, 37.768441 ] ] } } ] } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/index.html ================================================ arc.js
                            ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/index.js ================================================ module.exports = require('./arc'); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/package.json ================================================ { "name": "arc", "description": "draw great circle arcs", "url": "https://github.com/springmeyer/arc.js", "keywords": [ "maps", "spherical", "globe", "rhumb line", "crow flies", "great circle" ], "contributors": [ "Dane Springmeyer " ], "repository" : { "type" : "git", "url" : "git://github.com/springmeyer/arc.js.git" }, "version": "0.0.2", "licenses": [{ "type": "BSD" }], "main": "./index", "engines": { "node": ">=0.4.0" }, "dependencies": { }, "devDependencies": { } } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/polymaps.js ================================================ if (!org) var org = {}; if (!org.polymaps) org.polymaps = {}; (function(po){ po.version = "2.5.1"; // semver.org var zero = {x: 0, y: 0}; po.ns = { svg: "http://www.w3.org/2000/svg", xlink: "http://www.w3.org/1999/xlink" }; function ns(name) { var i = name.indexOf(":"); return i < 0 ? name : { space: po.ns[name.substring(0, i)], local: name.substring(i + 1) }; } po.id = (function() { var id = 0; return function() { return ++id; }; })(); po.svg = function(type) { return document.createElementNS(po.ns.svg, type); }; po.transform = function(a, b, c, d, e, f) { var transform = {}, zoomDelta, zoomFraction, k; if (!arguments.length) { a = 1; c = 0; e = 0; b = 0; d = 1; f = 0; } transform.zoomFraction = function(x) { if (!arguments.length) return zoomFraction; zoomFraction = x; zoomDelta = Math.floor(zoomFraction + Math.log(Math.sqrt(a * a + b * b + c * c + d * d)) / Math.LN2); k = Math.pow(2, -zoomDelta); return transform; }; transform.apply = function(x) { var k0 = Math.pow(2, -x.zoom), k1 = Math.pow(2, x.zoom - zoomDelta); return { column: (a * x.column * k0 + c * x.row * k0 + e) * k1, row: (b * x.column * k0 + d * x.row * k0 + f) * k1, zoom: x.zoom - zoomDelta }; }; transform.unapply = function(x) { var k0 = Math.pow(2, -x.zoom), k1 = Math.pow(2, x.zoom + zoomDelta); return { column: (x.column * k0 * d - x.row * k0 * c - e * d + f * c) / (a * d - b * c) * k1, row: (x.column * k0 * b - x.row * k0 * a - e * b + f * a) / (c * b - d * a) * k1, zoom: x.zoom + zoomDelta }; }; transform.toString = function() { return "matrix(" + [a * k, b * k, c * k, d * k].join(" ") + " 0 0)"; }; return transform.zoomFraction(0); }; po.cache = function(load, unload) { var cache = {}, locks = {}, map = {}, head = null, tail = null, size = 64, n = 0; function remove(tile) { n--; if (unload) unload(tile); delete map[tile.key]; if (tile.next) tile.next.prev = tile.prev; else if (tail = tile.prev) tail.next = null; if (tile.prev) tile.prev.next = tile.next; else if (head = tile.next) head.prev = null; } function flush() { for (var tile = tail; n > size; tile = tile.prev) { if (!tile) break; if (tile.lock) continue; remove(tile); } } cache.peek = function(c) { return map[[c.zoom, c.column, c.row].join("/")]; }; cache.load = function(c, projection) { var key = [c.zoom, c.column, c.row].join("/"), tile = map[key]; if (tile) { if (tile.prev) { tile.prev.next = tile.next; if (tile.next) tile.next.prev = tile.prev; else tail = tile.prev; tile.prev = null; tile.next = head; head.prev = tile; head = tile; } tile.lock = 1; locks[key] = tile; return tile; } tile = { key: key, column: c.column, row: c.row, zoom: c.zoom, next: head, prev: null, lock: 1 }; load.call(null, tile, projection); locks[key] = map[key] = tile; if (head) head.prev = tile; else tail = tile; head = tile; n++; return tile; }; cache.unload = function(key) { if (!(key in locks)) return false; var tile = locks[key]; tile.lock = 0; delete locks[key]; if (tile.request && tile.request.abort(false)) remove(tile); return tile; }; cache.locks = function() { return locks; }; cache.size = function(x) { if (!arguments.length) return size; size = x; flush(); return cache; }; cache.flush = function() { flush(); return cache; }; cache.clear = function() { for (var key in map) { var tile = map[key]; if (tile.request) tile.request.abort(false); if (unload) unload(map[key]); if (tile.lock) { tile.lock = 0; tile.element.parentNode.removeChild(tile.element); } } locks = {}; map = {}; head = tail = null; n = 0; return cache; }; return cache; }; po.url = function(template) { var hosts = [], repeat = true; function format(c) { var max = c.zoom < 0 ? 1 : 1 << c.zoom, column = c.column; if (repeat) { column = c.column % max; if (column < 0) column += max; } else if ((column < 0) || (column >= max)) { return null; } return template.replace(/{(.)}/g, function(s, v) { switch (v) { case "S": return hosts[(Math.abs(c.zoom) + c.row + column) % hosts.length]; case "Z": return c.zoom; case "X": return column; case "Y": return c.row; case "B": { var nw = po.map.coordinateLocation({row: c.row, column: column, zoom: c.zoom}), se = po.map.coordinateLocation({row: c.row + 1, column: column + 1, zoom: c.zoom}), pn = Math.ceil(Math.log(c.zoom) / Math.LN2); return se.lat.toFixed(pn) + "," + nw.lon.toFixed(pn) + "," + nw.lat.toFixed(pn) + "," + se.lon.toFixed(pn); } } return v; }); } format.template = function(x) { if (!arguments.length) return template; template = x; return format; }; format.hosts = function(x) { if (!arguments.length) return hosts; hosts = x; return format; }; format.repeat = function(x) { if (!arguments.length) return repeat; repeat = x; return format; }; return format; }; po.dispatch = function(that) { var types = {}; that.on = function(type, handler) { var listeners = types[type] || (types[type] = []); for (var i = 0; i < listeners.length; i++) { if (listeners[i].handler == handler) return that; // already registered } listeners.push({handler: handler, on: true}); return that; }; that.off = function(type, handler) { var listeners = types[type]; if (listeners) for (var i = 0; i < listeners.length; i++) { var l = listeners[i]; if (l.handler == handler) { l.on = false; listeners.splice(i, 1); break; } } return that; }; return function(event) { var listeners = types[event.type]; if (!listeners) return; listeners = listeners.slice(); // defensive copy for (var i = 0; i < listeners.length; i++) { var l = listeners[i]; if (l.on) l.handler.call(that, event); } }; }; po.queue = (function() { var queued = [], active = 0, size = 6; function process() { if ((active >= size) || !queued.length) return; active++; queued.pop()(); } function dequeue(send) { for (var i = 0; i < queued.length; i++) { if (queued[i] == send) { queued.splice(i, 1); return true; } } return false; } function request(url, callback, mimeType) { var req; function send() { req = new XMLHttpRequest(); if (mimeType && req.overrideMimeType) { req.overrideMimeType(mimeType); } req.open("GET", url, true); req.onreadystatechange = function(e) { if (req.readyState == 4) { active--; if (req.status < 300) callback(req); process(); } }; req.send(null); } function abort(hard) { if (dequeue(send)) return true; if (hard && req) { req.abort(); return true; } return false; } queued.push(send); process(); return {abort: abort}; } function text(url, callback, mimeType) { return request(url, function(req) { if (req.responseText) callback(req.responseText); }, mimeType); } /* * We the override MIME type here so that you can load local files; some * browsers don't assign a proper MIME type for local files. */ function json(url, callback) { return request(url, function(req) { if (req.responseText) callback(JSON.parse(req.responseText)); }, "application/json"); } function xml(url, callback) { return request(url, function(req) { if (req.responseXML) callback(req.responseXML); }, "application/xml"); } function image(image, src, callback) { var img; function send() { img = document.createElement("img"); img.onerror = function() { active--; process(); }; img.onload = function() { active--; callback(img); process(); }; img.src = src; image.setAttributeNS(po.ns.xlink, "href", src); } function abort(hard) { if (dequeue(send)) return true; if (hard && img) { img.src = "about:"; return true; } // cancels request return false; } queued.push(send); process(); return {abort: abort}; } return {text: text, xml: xml, json: json, image: image}; })(); po.map = function() { var map = {}, container, size, sizeActual = zero, sizeRadius = zero, // sizeActual / 2 tileSize = {x: 256, y: 256}, center = {lat: 37.76487, lon: -122.41948}, zoom = 12, zoomFraction = 0, zoomFactor = 1, // Math.pow(2, zoomFraction) zoomRange = [1, 18], angle = 0, angleCos = 1, // Math.cos(angle) angleSin = 0, // Math.sin(angle) angleCosi = 1, // Math.cos(-angle) angleSini = 0, // Math.sin(-angle) ymin = -180, // lat2y(centerRange[0].lat) ymax = 180; // lat2y(centerRange[1].lat) var centerRange = [ {lat: y2lat(ymin), lon: -Infinity}, {lat: y2lat(ymax), lon: Infinity} ]; map.locationCoordinate = function(l) { var c = po.map.locationCoordinate(l), k = Math.pow(2, zoom); c.column *= k; c.row *= k; c.zoom += zoom; return c; }; map.coordinateLocation = po.map.coordinateLocation; map.coordinatePoint = function(tileCenter, c) { var kc = Math.pow(2, zoom - c.zoom), kt = Math.pow(2, zoom - tileCenter.zoom), dx = (c.column * kc - tileCenter.column * kt) * tileSize.x * zoomFactor, dy = (c.row * kc - tileCenter.row * kt) * tileSize.y * zoomFactor; return { x: sizeRadius.x + angleCos * dx - angleSin * dy, y: sizeRadius.y + angleSin * dx + angleCos * dy }; }; map.pointCoordinate = function(tileCenter, p) { var kt = Math.pow(2, zoom - tileCenter.zoom), dx = (p.x - sizeRadius.x) / zoomFactor, dy = (p.y - sizeRadius.y) / zoomFactor; return { column: tileCenter.column * kt + (angleCosi * dx - angleSini * dy) / tileSize.x, row: tileCenter.row * kt + (angleSini * dx + angleCosi * dy) / tileSize.y, zoom: zoom }; }; map.locationPoint = function(l) { var k = Math.pow(2, zoom + zoomFraction - 3) / 45, dx = (l.lon - center.lon) * k * tileSize.x, dy = (lat2y(center.lat) - lat2y(l.lat)) * k * tileSize.y; return { x: sizeRadius.x + angleCos * dx - angleSin * dy, y: sizeRadius.y + angleSin * dx + angleCos * dy }; }; map.pointLocation = function(p) { var k = 45 / Math.pow(2, zoom + zoomFraction - 3), dx = (p.x - sizeRadius.x) * k, dy = (p.y - sizeRadius.y) * k; return { lon: center.lon + (angleCosi * dx - angleSini * dy) / tileSize.x, lat: y2lat(lat2y(center.lat) - (angleSini * dx + angleCosi * dy) / tileSize.y) }; }; function rezoom() { if (zoomRange) { if (zoom < zoomRange[0]) zoom = zoomRange[0]; else if (zoom > zoomRange[1]) zoom = zoomRange[1]; } zoomFraction = zoom - (zoom = Math.round(zoom)); zoomFactor = Math.pow(2, zoomFraction); } function recenter() { if (!centerRange) return; var k = 45 / Math.pow(2, zoom + zoomFraction - 3); // constrain latitude var y = Math.max(Math.abs(angleSin * sizeRadius.x + angleCos * sizeRadius.y), Math.abs(angleSini * sizeRadius.x + angleCosi * sizeRadius.y)), lat0 = y2lat(ymin - y * k / tileSize.y), lat1 = y2lat(ymax + y * k / tileSize.y); center.lat = Math.max(lat0, Math.min(lat1, center.lat)); // constrain longitude var x = Math.max(Math.abs(angleSin * sizeRadius.y + angleCos * sizeRadius.x), Math.abs(angleSini * sizeRadius.y + angleCosi * sizeRadius.x)), lon0 = centerRange[0].lon - x * k / tileSize.x, lon1 = centerRange[1].lon + x * k / tileSize.x; center.lon = Math.max(lon0, Math.min(lon1, center.lon)); } // a place to capture mouse events if no tiles exist var rect = po.svg("rect"); rect.setAttribute("visibility", "hidden"); rect.setAttribute("pointer-events", "all"); map.container = function(x) { if (!arguments.length) return container; container = x; container.setAttribute("class", "map"); container.appendChild(rect); return map.resize(); // infer size }; map.focusableParent = function() { for (var p = container; p; p = p.parentNode) { if (p.tabIndex >= 0) return p; } return window; }; map.mouse = function(e) { var point = (container.ownerSVGElement || container).createSVGPoint(); if ((bug44083 < 0) && (window.scrollX || window.scrollY)) { var svg = document.body.appendChild(po.svg("svg")); svg.style.position = "absolute"; svg.style.top = svg.style.left = "0px"; var ctm = svg.getScreenCTM(); bug44083 = !(ctm.f || ctm.e); document.body.removeChild(svg); } if (bug44083) { point.x = e.pageX; point.y = e.pageY; } else { point.x = e.clientX; point.y = e.clientY; } return point.matrixTransform(container.getScreenCTM().inverse()); }; map.size = function(x) { if (!arguments.length) return sizeActual; size = x; return map.resize(); // size tiles }; map.resize = function() { if (!size) { rect.setAttribute("width", "100%"); rect.setAttribute("height", "100%"); b = rect.getBBox(); sizeActual = {x: b.width, y: b.height}; resizer.add(map); } else { sizeActual = size; resizer.remove(map); } rect.setAttribute("width", sizeActual.x); rect.setAttribute("height", sizeActual.y); sizeRadius = {x: sizeActual.x / 2, y: sizeActual.y / 2}; recenter(); map.dispatch({type: "resize"}); return map; }; map.tileSize = function(x) { if (!arguments.length) return tileSize; tileSize = x; map.dispatch({type: "move"}); return map; }; map.center = function(x) { if (!arguments.length) return center; center = x; recenter(); map.dispatch({type: "move"}); return map; }; map.panBy = function(x) { var k = 45 / Math.pow(2, zoom + zoomFraction - 3), dx = x.x * k, dy = x.y * k; return map.center({ lon: center.lon + (angleSini * dy - angleCosi * dx) / tileSize.x, lat: y2lat(lat2y(center.lat) + (angleSini * dx + angleCosi * dy) / tileSize.y) }); }; map.centerRange = function(x) { if (!arguments.length) return centerRange; centerRange = x; if (centerRange) { ymin = centerRange[0].lat > -90 ? lat2y(centerRange[0].lat) : -Infinity; ymax = centerRange[0].lat < 90 ? lat2y(centerRange[1].lat) : Infinity; } else { ymin = -Infinity; ymax = Infinity; } recenter(); map.dispatch({type: "move"}); return map; }; map.zoom = function(x) { if (!arguments.length) return zoom + zoomFraction; zoom = x; rezoom(); return map.center(center); }; map.zoomBy = function(z, x0, l) { if (arguments.length < 2) return map.zoom(zoom + zoomFraction + z); // compute the location of x0 if (arguments.length < 3) l = map.pointLocation(x0); // update the zoom level zoom = zoom + zoomFraction + z; rezoom(); // compute the new point of the location var x1 = map.locationPoint(l); return map.panBy({x: x0.x - x1.x, y: x0.y - x1.y}); }; map.zoomRange = function(x) { if (!arguments.length) return zoomRange; zoomRange = x; return map.zoom(zoom + zoomFraction); }; map.extent = function(x) { if (!arguments.length) return [ map.pointLocation({x: 0, y: sizeActual.y}), map.pointLocation({x: sizeActual.x, y: 0}) ]; // compute the extent in points, scale factor, and center var bl = map.locationPoint(x[0]), tr = map.locationPoint(x[1]), k = Math.max((tr.x - bl.x) / sizeActual.x, (bl.y - tr.y) / sizeActual.y), l = map.pointLocation({x: (bl.x + tr.x) / 2, y: (bl.y + tr.y) / 2}); // update the zoom level zoom = zoom + zoomFraction - Math.log(k) / Math.LN2; rezoom(); // set the new center return map.center(l); }; map.angle = function(x) { if (!arguments.length) return angle; angle = x; angleCos = Math.cos(angle); angleSin = Math.sin(angle); angleCosi = Math.cos(-angle); angleSini = Math.sin(-angle); recenter(); map.dispatch({type: "move"}); return map; }; map.add = function(x) { x.map(map); return map; }; map.remove = function(x) { x.map(null); return map; }; map.dispatch = po.dispatch(map); return map; }; function resizer(e) { for (var i = 0; i < resizer.maps.length; i++) { resizer.maps[i].resize(); } } resizer.maps = []; resizer.add = function(map) { for (var i = 0; i < resizer.maps.length; i++) { if (resizer.maps[i] == map) return; } resizer.maps.push(map); }; resizer.remove = function(map) { for (var i = 0; i < resizer.maps.length; i++) { if (resizer.maps[i] == map) { resizer.maps.splice(i, 1); return; } } }; // Note: assumes single window (no frames, iframes, etc.)! if (window.addEventListener) { window.addEventListener("resize", resizer, false); window.addEventListener("load", resizer, false); } // See http://wiki.openstreetmap.org/wiki/Mercator function y2lat(y) { return 360 / Math.PI * Math.atan(Math.exp(y * Math.PI / 180)) - 90; } function lat2y(lat) { return 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)); } po.map.locationCoordinate = function(l) { var k = 1 / 360; return { column: (l.lon + 180) * k, row: (180 - lat2y(l.lat)) * k, zoom: 0 }; }; po.map.coordinateLocation = function(c) { var k = 45 / Math.pow(2, c.zoom - 3); return { lon: k * c.column - 180, lat: y2lat(180 - k * c.row) }; }; // https://bugs.webkit.org/show_bug.cgi?id=44083 var bug44083 = /WebKit/.test(navigator.userAgent) ? -1 : 0; po.layer = function(load, unload) { var layer = {}, cache = layer.cache = po.cache(load, unload).size(512), tile = true, visible = true, zoom, id, map, container = po.svg("g"), transform, levelZoom, levels = {}; container.setAttribute("class", "layer"); for (var i = -4; i <= -1; i++) levels[i] = container.appendChild(po.svg("g")); for (var i = 2; i >= 1; i--) levels[i] = container.appendChild(po.svg("g")); levels[0] = container.appendChild(po.svg("g")); function zoomIn(z) { var end = levels[0].nextSibling; for (; levelZoom < z; levelZoom++) { // -4, -3, -2, -1, +2, +1, =0 // current order // -3, -2, -1, +2, +1, =0, -4 // insertBefore(-4, end) // -3, -2, -1, +1, =0, -4, +2 // insertBefore(+2, end) // -3, -2, -1, =0, -4, +2, +1 // insertBefore(+1, end) // -4, -3, -2, -1, +2, +1, =0 // relabel container.insertBefore(levels[-4], end); container.insertBefore(levels[2], end); container.insertBefore(levels[1], end); var t = levels[-4]; for (var dz = -4; dz < 2;) levels[dz] = levels[++dz]; levels[dz] = t; } } function zoomOut(z) { var end = levels[0].nextSibling; for (; levelZoom > z; levelZoom--) { // -4, -3, -2, -1, +2, +1, =0 // current order // -4, -3, -2, +2, +1, =0, -1 // insertBefore(-1, end) // +2, -4, -3, -2, +1, =0, -1 // insertBefore(+2, -4) // -4, -3, -2, -1, +2, +1, =0 // relabel container.insertBefore(levels[-1], end); container.insertBefore(levels[2], levels[-4]); var t = levels[2]; for (var dz = 2; dz > -4;) levels[dz] = levels[--dz]; levels[dz] = t; } } function move() { var map = layer.map(), // in case the layer is removed mapZoom = map.zoom(), mapZoomFraction = mapZoom - (mapZoom = Math.round(mapZoom)), mapSize = map.size(), mapAngle = map.angle(), tileSize = map.tileSize(), tileCenter = map.locationCoordinate(map.center()); // set the layer zoom levels if (levelZoom != mapZoom) { if (levelZoom < mapZoom) zoomIn(mapZoom); else if (levelZoom > mapZoom) zoomOut(mapZoom); else levelZoom = mapZoom; for (var z = -4; z <= 2; z++) { var l = levels[z]; l.setAttribute("class", "zoom" + (z < 0 ? "" : "+") + z + " zoom" + (mapZoom + z)); l.setAttribute("transform", "scale(" + Math.pow(2, -z) + ")"); } } // set the layer transform container.setAttribute("transform", "translate(" + (mapSize.x / 2) + "," + (mapSize.y / 2) + ")" + (mapAngle ? "rotate(" + mapAngle / Math.PI * 180 + ")" : "") + (mapZoomFraction ? "scale(" + Math.pow(2, mapZoomFraction) + ")" : "") + (transform ? transform.zoomFraction(mapZoomFraction) : "")); // get the coordinates of the four corners var c0 = map.pointCoordinate(tileCenter, zero), c1 = map.pointCoordinate(tileCenter, {x: mapSize.x, y: 0}), c2 = map.pointCoordinate(tileCenter, mapSize), c3 = map.pointCoordinate(tileCenter, {x: 0, y: mapSize.y}); // round to pixel boundary to avoid anti-aliasing artifacts if (!mapZoomFraction && !mapAngle && !transform) { tileCenter.column = (Math.round(tileSize.x * tileCenter.column) + (mapSize.x & 1) / 2) / tileSize.x; tileCenter.row = (Math.round(tileSize.y * tileCenter.row) + (mapSize.y & 1) / 2) / tileSize.y; } // layer-specific coordinate transform if (transform) { c0 = transform.unapply(c0); c1 = transform.unapply(c1); c2 = transform.unapply(c2); c3 = transform.unapply(c3); tileCenter = transform.unapply(tileCenter); } // layer-specific zoom transform var tileLevel = zoom ? zoom(c0.zoom) - c0.zoom : 0; if (tileLevel) { var k = Math.pow(2, tileLevel); c0.column *= k; c0.row *= k; c1.column *= k; c1.row *= k; c2.column *= k; c2.row *= k; c3.column *= k; c3.row *= k; c0.zoom = c1.zoom = c2.zoom = c3.zoom += tileLevel; } // tile-specific projection function projection(c) { var zoom = c.zoom, max = zoom < 0 ? 1 : 1 << zoom, column = c.column % max, row = c.row; if (column < 0) column += max; return { locationPoint: function(l) { var c = po.map.locationCoordinate(l), k = Math.pow(2, zoom - c.zoom); return { x: tileSize.x * (k * c.column - column), y: tileSize.y * (k * c.row - row) }; } }; } // record which tiles are visible var oldLocks = cache.locks(), newLocks = {}; // reset the proxy counts for (var key in oldLocks) { oldLocks[key].proxyCount = 0; } // load the tiles! if (visible && tileLevel > -5 && tileLevel < 3) { var ymax = c0.zoom < 0 ? 1 : 1 << c0.zoom; if (tile) { scanTriangle(c0, c1, c2, 0, ymax, scanLine); scanTriangle(c2, c3, c0, 0, ymax, scanLine); } else { var x = Math.floor((c0.column + c2.column) / 2), y = Math.max(0, Math.min(ymax - 1, Math.floor((c1.row + c3.row) / 2))), z = Math.min(4, c0.zoom); x = x >> z << z; y = y >> z << z; scanLine(x, x + 1, y); } } // scan-line conversion function scanLine(x0, x1, y) { var z = c0.zoom, z0 = 2 - tileLevel, z1 = 4 + tileLevel; for (var x = x0; x < x1; x++) { var t = cache.load({column: x, row: y, zoom: z}, projection); if (!t.ready && !(t.key in newLocks)) { t.proxyRefs = {}; var c, full, proxy; // downsample high-resolution tiles for (var dz = 1; dz <= z0; dz++) { full = true; for (var dy = 0, k = 1 << dz; dy <= k; dy++) { for (var dx = 0; dx <= k; dx++) { proxy = cache.peek(c = { column: (x << dz) + dx, row: (y << dz) + dy, zoom: z + dz }); if (proxy && proxy.ready) { newLocks[proxy.key] = cache.load(c); proxy.proxyCount++; t.proxyRefs[proxy.key] = proxy; } else { full = false; } } } if (full) break; } // upsample low-resolution tiles if (!full) { for (var dz = 1; dz <= z1; dz++) { proxy = cache.peek(c = { column: x >> dz, row: y >> dz, zoom: z - dz }); if (proxy && proxy.ready) { newLocks[proxy.key] = cache.load(c); proxy.proxyCount++; t.proxyRefs[proxy.key] = proxy; break; } } } } newLocks[t.key] = t; } } // position tiles for (var key in newLocks) { var t = newLocks[key], k = Math.pow(2, t.level = t.zoom - tileCenter.zoom); t.element.setAttribute("transform", "translate(" + (t.x = tileSize.x * (t.column - tileCenter.column * k)) + "," + (t.y = tileSize.y * (t.row - tileCenter.row * k)) + ")"); } // remove tiles that are no longer visible for (var key in oldLocks) { if (!(key in newLocks)) { var t = cache.unload(key); t.element.parentNode.removeChild(t.element); delete t.proxyRefs; } } // append tiles that are now visible for (var key in newLocks) { var t = newLocks[key]; if (t.element.parentNode != levels[t.level]) { levels[t.level].appendChild(t.element); if (layer.show) layer.show(t); } } // flush the cache, clearing no-longer-needed tiles cache.flush(); // dispatch the move event layer.dispatch({type: "move"}); } // remove proxy tiles when tiles load function cleanup(e) { if (e.tile.proxyRefs) { for (var proxyKey in e.tile.proxyRefs) { var proxyTile = e.tile.proxyRefs[proxyKey]; if ((--proxyTile.proxyCount <= 0) && cache.unload(proxyKey)) { proxyTile.element.parentNode.removeChild(proxyTile.element); } } delete e.tile.proxyRefs; } } layer.map = function(x) { if (!arguments.length) return map; if (map) { if (map == x) { container.parentNode.appendChild(container); // move to end return layer; } map.off("move", move).off("resize", move); container.parentNode.removeChild(container); } map = x; if (map) { map.container().appendChild(container); if (layer.init) layer.init(container); map.on("move", move).on("resize", move); move(); } return layer; }; layer.container = function() { return container; }; layer.levels = function() { return levels; }; layer.id = function(x) { if (!arguments.length) return id; id = x; container.setAttribute("id", x); return layer; }; layer.visible = function(x) { if (!arguments.length) return visible; if (visible = x) container.removeAttribute("visibility") else container.setAttribute("visibility", "hidden"); if (map) move(); return layer; }; layer.transform = function(x) { if (!arguments.length) return transform; transform = x; if (map) move(); return layer; }; layer.zoom = function(x) { if (!arguments.length) return zoom; zoom = typeof x == "function" || x == null ? x : function() { return x; }; if (map) move(); return layer; }; layer.tile = function(x) { if (!arguments.length) return tile; tile = x; if (map) move(); return layer; }; layer.reload = function() { cache.clear(); if (map) move(); return layer; }; layer.dispatch = po.dispatch(layer); layer.on("load", cleanup); return layer; }; // scan-line conversion function edge(a, b) { if (a.row > b.row) { var t = a; a = b; b = t; } return { x0: a.column, y0: a.row, x1: b.column, y1: b.row, dx: b.column - a.column, dy: b.row - a.row }; } // scan-line conversion function scanSpans(e0, e1, ymin, ymax, scanLine) { var y0 = Math.max(ymin, Math.floor(e1.y0)), y1 = Math.min(ymax, Math.ceil(e1.y1)); // sort edges by x-coordinate if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ? (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) : (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) { var t = e0; e0 = e1; e1 = t; } // scan lines! var m0 = e0.dx / e0.dy, m1 = e1.dx / e1.dy, d0 = e0.dx > 0, // use y + 1 to compute x0 d1 = e1.dx < 0; // use y + 1 to compute x1 for (var y = y0; y < y1; y++) { var x0 = m0 * Math.max(0, Math.min(e0.dy, y + d0 - e0.y0)) + e0.x0, x1 = m1 * Math.max(0, Math.min(e1.dy, y + d1 - e1.y0)) + e1.x0; scanLine(Math.floor(x1), Math.ceil(x0), y); } } // scan-line conversion function scanTriangle(a, b, c, ymin, ymax, scanLine) { var ab = edge(a, b), bc = edge(b, c), ca = edge(c, a); // sort edges by y-length if (ab.dy > bc.dy) { var t = ab; ab = bc; bc = t; } if (ab.dy > ca.dy) { var t = ab; ab = ca; ca = t; } if (bc.dy > ca.dy) { var t = bc; bc = ca; ca = t; } // scan span! scan span! if (ab.dy) scanSpans(ca, ab, ymin, ymax, scanLine); if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine); } po.image = function() { var image = po.layer(load, unload), url; function load(tile) { var element = tile.element = po.svg("image"), size = image.map().tileSize(); element.setAttribute("preserveAspectRatio", "none"); element.setAttribute("width", size.x); element.setAttribute("height", size.y); if (typeof url == "function") { element.setAttribute("opacity", 0); var tileUrl = url(tile); if (tileUrl != null) { tile.request = po.queue.image(element, tileUrl, function(img) { delete tile.request; tile.ready = true; tile.img = img; element.removeAttribute("opacity"); image.dispatch({type: "load", tile: tile}); }); } else { tile.ready = true; image.dispatch({type: "load", tile: tile}); } } else { tile.ready = true; if (url != null) element.setAttributeNS(po.ns.xlink, "href", url); image.dispatch({type: "load", tile: tile}); } } function unload(tile) { if (tile.request) tile.request.abort(true); } image.url = function(x) { if (!arguments.length) return url; url = typeof x == "string" && /{.}/.test(x) ? po.url(x) : x; return image.reload(); }; return image; }; po.geoJson = function(fetch) { var geoJson = po.layer(load, unload), container = geoJson.container(), url, clip = true, clipId = "org.polymaps." + po.id(), clipHref = "url(#" + clipId + ")", clipPath = container.insertBefore(po.svg("clipPath"), container.firstChild), clipRect = clipPath.appendChild(po.svg("rect")), scale = "auto", zoom = null, features; container.setAttribute("fill-rule", "evenodd"); clipPath.setAttribute("id", clipId); if (!arguments.length) fetch = po.queue.json; function projection(proj) { var l = {lat: 0, lon: 0}; return function(coordinates) { l.lat = coordinates[1]; l.lon = coordinates[0]; var p = proj(l); coordinates.x = p.x; coordinates.y = p.y; return p; }; } function geometry(o, proj) { return o && o.type in types && types[o.type](o, proj); } var types = { Point: function(o, proj) { var p = proj(o.coordinates), c = po.svg("circle"); c.setAttribute("r", 4.5); c.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); return c; }, MultiPoint: function(o, proj) { var g = po.svg("g"), c = o.coordinates, p, // proj(c[i]) x, // svg:circle i = -1, n = c.length; while (++i < n) { x = g.appendChild(po.svg("circle")); x.setAttribute("r", 4.5); x.setAttribute("transform", "translate(" + (p = proj(c[i])).x + "," + p.y + ")"); } return g; }, LineString: function(o, proj) { var x = po.svg("path"), d = ["M"], c = o.coordinates, p, // proj(c[i]) i = -1, n = c.length; while (++i < n) d.push((p = proj(c[i])).x, ",", p.y, "L"); d.pop(); if (!d.length) return; x.setAttribute("d", d.join("")); return x; }, MultiLineString: function(o, proj) { var x = po.svg("path"), d = [], ci = o.coordinates, cj, // ci[i] i = -1, j, n = ci.length, m; while (++i < n) { cj = ci[i]; j = -1; m = cj.length; d.push("M"); while (++j < m) d.push((p = proj(cj[j])).x, ",", p.y, "L"); d.pop(); } if (!d.length) return; x.setAttribute("d", d.join("")); return x; }, Polygon: function(o, proj) { var x = po.svg("path"), d = [], ci = o.coordinates, cj, // ci[i] i = -1, j, n = ci.length, m; while (++i < n) { cj = ci[i]; j = -1; m = cj.length - 1; d.push("M"); while (++j < m) d.push((p = proj(cj[j])).x, ",", p.y, "L"); d[d.length - 1] = "Z"; } if (!d.length) return; x.setAttribute("d", d.join("")); return x; }, MultiPolygon: function(o, proj) { var x = po.svg("path"), d = [], ci = o.coordinates, cj, // ci[i] ck, // cj[j] i = -1, j, k, n = ci.length, m, l; while (++i < n) { cj = ci[i]; j = -1; m = cj.length; while (++j < m) { ck = cj[j]; k = -1; l = ck.length - 1; d.push("M"); while (++k < l) d.push((p = proj(ck[k])).x, ",", p.y, "L"); d[d.length - 1] = "Z"; } } if (!d.length) return; x.setAttribute("d", d.join("")); return x; }, GeometryCollection: function(o, proj) { var g = po.svg("g"), i = -1, c = o.geometries, n = c.length, x; while (++i < n) { x = geometry(c[i], proj); if (x) g.appendChild(x); } return g; } }; function rescale(o, e, k) { return o.type in rescales && rescales[o.type](o, e, k); } var rescales = { Point: function (o, e, k) { var p = o.coordinates; e.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k); }, MultiPoint: function (o, e, k) { var c = o.coordinates, i = -1, n = p.length, x = e.firstChild, p; while (++i < n) { p = c[i]; x.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k); x = x.nextSibling; } } }; function load(tile, proj) { var g = tile.element = po.svg("g"); tile.features = []; proj = projection(proj(tile).locationPoint); function update(data) { var updated = []; /* Fetch the next batch of features, if so directed. */ if (data.next) tile.request = fetch(data.next.href, update); /* Convert the GeoJSON to SVG. */ switch (data.type) { case "FeatureCollection": { for (var i = 0; i < data.features.length; i++) { var feature = data.features[i], element = geometry(feature.geometry, proj); if (element) updated.push({element: g.appendChild(element), data: feature}); } break; } case "Feature": { var element = geometry(data.geometry, proj); if (element) updated.push({element: g.appendChild(element), data: data}); break; } default: { var element = geometry(data, proj); if (element) updated.push({element: g.appendChild(element), data: {type: "Feature", geometry: data}}); break; } } tile.ready = true; updated.push.apply(tile.features, updated); geoJson.dispatch({type: "load", tile: tile, features: updated}); } if (url != null) { tile.request = fetch(typeof url == "function" ? url(tile) : url, update); } else { update({type: "FeatureCollection", features: features || []}); } } function unload(tile) { if (tile.request) tile.request.abort(true); } function move() { var zoom = geoJson.map().zoom(), tiles = geoJson.cache.locks(), // visible tiles key, // key in locks tile, // locks[key] features, // tile.features i, // current feature index n, // current feature count, features.length feature, // features[i] k; // scale transform if (scale == "fixed") { for (key in tiles) { if ((tile = tiles[key]).scale != zoom) { k = "scale(" + Math.pow(2, tile.zoom - zoom) + ")"; i = -1; n = (features = tile.features).length; while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, k); tile.scale = zoom; } } } else { for (key in tiles) { i = -1; n = (features = (tile = tiles[key]).features).length; while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, ""); delete tile.scale; } } } geoJson.url = function(x) { if (!arguments.length) return url; url = typeof x == "string" && /{.}/.test(x) ? po.url(x) : x; if (url != null) features = null; if (typeof url == "string") geoJson.tile(false); return geoJson.reload(); }; geoJson.features = function(x) { if (!arguments.length) return features; if (features = x) { url = null; geoJson.tile(false); } return geoJson.reload(); }; geoJson.clip = function(x) { if (!arguments.length) return clip; if (clip) container.removeChild(clipPath); if (clip = x) container.insertBefore(clipPath, container.firstChild); var locks = geoJson.cache.locks(); for (var key in locks) { if (clip) locks[key].element.setAttribute("clip-path", clipHref); else locks[key].element.removeAttribute("clip-path"); } return geoJson; }; var __tile__ = geoJson.tile; geoJson.tile = function(x) { if (arguments.length && !x) geoJson.clip(x); return __tile__.apply(geoJson, arguments); }; var __map__ = geoJson.map; geoJson.map = function(x) { if (x && clipRect) { var size = x.tileSize(); clipRect.setAttribute("width", size.x); clipRect.setAttribute("height", size.y); } return __map__.apply(geoJson, arguments); }; geoJson.scale = function(x) { if (!arguments.length) return scale; if (scale = x) geoJson.on("move", move); else geoJson.off("move", move); if (geoJson.map()) move(); return geoJson; }; geoJson.show = function(tile) { if (clip) tile.element.setAttribute("clip-path", clipHref); else tile.element.removeAttribute("clip-path"); geoJson.dispatch({type: "show", tile: tile, features: tile.features}); return geoJson; }; geoJson.reshow = function() { var locks = geoJson.cache.locks(); for (var key in locks) geoJson.show(locks[key]); return geoJson; }; return geoJson; }; po.dblclick = function() { var dblclick = {}, zoom = "mouse", map, container; function handle(e) { var z = map.zoom(); if (e.shiftKey) z = Math.ceil(z) - z - 1; else z = 1 - z + Math.floor(z); zoom === "mouse" ? map.zoomBy(z, map.mouse(e)) : map.zoomBy(z); } dblclick.zoom = function(x) { if (!arguments.length) return zoom; zoom = x; return dblclick; }; dblclick.map = function(x) { if (!arguments.length) return map; if (map) { container.removeEventListener("dblclick", handle, false); container = null; } if (map = x) { container = map.container(); container.addEventListener("dblclick", handle, false); } return dblclick; }; return dblclick; }; po.drag = function() { var drag = {}, map, container, dragging; function mousedown(e) { if (e.shiftKey) return; dragging = { x: e.clientX, y: e.clientY }; map.focusableParent().focus(); e.preventDefault(); document.body.style.setProperty("cursor", "move", null); } function mousemove(e) { if (!dragging) return; map.panBy({x: e.clientX - dragging.x, y: e.clientY - dragging.y}); dragging.x = e.clientX; dragging.y = e.clientY; } function mouseup(e) { if (!dragging) return; mousemove(e); dragging = null; document.body.style.removeProperty("cursor"); } drag.map = function(x) { if (!arguments.length) return map; if (map) { container.removeEventListener("mousedown", mousedown, false); container = null; } if (map = x) { container = map.container(); container.addEventListener("mousedown", mousedown, false); } return drag; }; window.addEventListener("mousemove", mousemove, false); window.addEventListener("mouseup", mouseup, false); return drag; }; po.wheel = function() { var wheel = {}, timePrev = 0, last = 0, smooth = true, zoom = "mouse", location, map, container; function move(e) { location = null; } // mousewheel events are totally broken! // https://bugs.webkit.org/show_bug.cgi?id=40441 // not only that, but Chrome and Safari differ in re. to acceleration! var inner = document.createElement("div"), outer = document.createElement("div"); outer.style.visibility = "hidden"; outer.style.top = "0px"; outer.style.height = "0px"; outer.style.width = "0px"; outer.style.overflowY = "scroll"; inner.style.height = "2000px"; outer.appendChild(inner); document.body.appendChild(outer); function mousewheel(e) { var delta = e.wheelDelta || -e.detail, point; /* Detect the pixels that would be scrolled by this wheel event. */ if (delta) { if (smooth) { try { outer.scrollTop = 1000; outer.dispatchEvent(e); delta = 1000 - outer.scrollTop; } catch (error) { // Derp! Hope for the best? } delta *= .005; } /* If smooth zooming is disabled, batch events into unit steps. */ else { var timeNow = Date.now(); if (timeNow - timePrev > 200) { delta = delta > 0 ? +1 : -1; timePrev = timeNow; } else { delta = 0; } } } if (delta) { switch (zoom) { case "mouse": { point = map.mouse(e); if (!location) location = map.pointLocation(point); map.off("move", move).zoomBy(delta, point, location).on("move", move); break; } case "location": { map.zoomBy(delta, map.locationPoint(location), location); break; } default: { // center map.zoomBy(delta); break; } } } e.preventDefault(); return false; // for Firefox } wheel.smooth = function(x) { if (!arguments.length) return smooth; smooth = x; return wheel; }; wheel.zoom = function(x, l) { if (!arguments.length) return zoom; zoom = x; location = l; if (map) { if (zoom == "mouse") map.on("move", move); else map.off("move", move); } return wheel; }; wheel.map = function(x) { if (!arguments.length) return map; if (map) { container.removeEventListener("mousemove", move, false); container.removeEventListener("mousewheel", mousewheel, false); container.removeEventListener("MozMousePixelScroll", mousewheel, false); container = null; map.off("move", move); } if (map = x) { if (zoom == "mouse") map.on("move", move); container = map.container(); container.addEventListener("mousemove", move, false); container.addEventListener("mousewheel", mousewheel, false); container.addEventListener("MozMousePixelScroll", mousewheel, false); } return wheel; }; return wheel; }; po.arrow = function() { var arrow = {}, key = {left: 0, right: 0, up: 0, down: 0}, last = 0, repeatTimer, repeatDelay = 250, repeatInterval = 50, speed = 16, map, parent; function keydown(e) { if (e.ctrlKey || e.altKey || e.metaKey) return; var now = Date.now(), dx = 0, dy = 0; switch (e.keyCode) { case 37: { if (!key.left) { last = now; key.left = 1; if (!key.right) dx = speed; } break; } case 39: { if (!key.right) { last = now; key.right = 1; if (!key.left) dx = -speed; } break; } case 38: { if (!key.up) { last = now; key.up = 1; if (!key.down) dy = speed; } break; } case 40: { if (!key.down) { last = now; key.down = 1; if (!key.up) dy = -speed; } break; } default: return; } if (dx || dy) map.panBy({x: dx, y: dy}); if (!repeatTimer && (key.left | key.right | key.up | key.down)) { repeatTimer = setInterval(repeat, repeatInterval); } e.preventDefault(); } function keyup(e) { last = Date.now(); switch (e.keyCode) { case 37: key.left = 0; break; case 39: key.right = 0; break; case 38: key.up = 0; break; case 40: key.down = 0; break; default: return; } if (repeatTimer && !(key.left | key.right | key.up | key.down)) { repeatTimer = clearInterval(repeatTimer); } e.preventDefault(); } function keypress(e) { switch (e.charCode) { case 45: case 95: map.zoom(Math.ceil(map.zoom()) - 1); break; // - _ case 43: case 61: map.zoom(Math.floor(map.zoom()) + 1); break; // = + default: return; } e.preventDefault(); } function repeat() { if (!map) return; if (Date.now() < last + repeatDelay) return; var dx = (key.left - key.right) * speed, dy = (key.up - key.down) * speed; if (dx || dy) map.panBy({x: dx, y: dy}); } arrow.map = function(x) { if (!arguments.length) return map; if (map) { parent.removeEventListener("keypress", keypress, false); parent.removeEventListener("keydown", keydown, false); parent.removeEventListener("keyup", keyup, false); parent = null; } if (map = x) { parent = map.focusableParent(); parent.addEventListener("keypress", keypress, false); parent.addEventListener("keydown", keydown, false); parent.addEventListener("keyup", keyup, false); } return arrow; }; arrow.speed = function(x) { if (!arguments.length) return speed; speed = x; return arrow; }; return arrow; }; po.hash = function() { var hash = {}, s0, // cached location.hash lat = 90 - 1e-8, // allowable latitude range map; var parser = function(map, s) { var args = s.split("/").map(Number); if (args.length < 3 || args.some(isNaN)) return true; // replace bogus hash else { var size = map.size(); map.zoomBy(args[0] - map.zoom(), {x: size.x / 2, y: size.y / 2}, {lat: Math.min(lat, Math.max(-lat, args[1])), lon: args[2]}); } }; var formatter = function(map) { var center = map.center(), zoom = map.zoom(), precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); return "#" + zoom.toFixed(2) + "/" + center.lat.toFixed(precision) + "/" + center.lon.toFixed(precision); }; function move() { var s1 = formatter(map); if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map! } function hashchange() { if (location.hash === s0) return; // ignore spurious hashchange events if (parser(map, (s0 = location.hash).substring(1))) move(); // replace bogus hash } hash.map = function(x) { if (!arguments.length) return map; if (map) { map.off("move", move); window.removeEventListener("hashchange", hashchange, false); } if (map = x) { map.on("move", move); window.addEventListener("hashchange", hashchange, false); location.hash ? hashchange() : move(); } return hash; }; hash.parser = function(x) { if (!arguments.length) return parser; parser = x; return hash; }; hash.formatter = function(x) { if (!arguments.length) return formatter; formatter = x; return hash; }; return hash; }; po.touch = function() { var touch = {}, map, container, rotate = false, last = 0, zoom, angle, locations = {}; // touch identifier -> location function touchstart(e) { var i = -1, n = e.touches.length, t = Date.now(); // doubletap detection if ((n == 1) && (t - last < 300)) { var z = map.zoom(); map.zoomBy(1 - z + Math.floor(z), map.mouse(e.touches[0])); e.preventDefault(); } last = t; // store original zoom & touch locations zoom = map.zoom(); angle = map.angle(); while (++i < n) { t = e.touches[i]; locations[t.identifier] = map.pointLocation(map.mouse(t)); } } function touchmove(e) { switch (e.touches.length) { case 1: { // single-touch pan var t0 = e.touches[0]; map.zoomBy(0, map.mouse(t0), locations[t0.identifier]); e.preventDefault(); break; } case 2: { // double-touch pan + zoom + rotate var t0 = e.touches[0], t1 = e.touches[1], p0 = map.mouse(t0), p1 = map.mouse(t1), p2 = {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2}, // center point c0 = po.map.locationCoordinate(locations[t0.identifier]), c1 = po.map.locationCoordinate(locations[t1.identifier]), c2 = {row: (c0.row + c1.row) / 2, column: (c0.column + c1.column) / 2, zoom: 0}, l2 = po.map.coordinateLocation(c2); // center location map.zoomBy(Math.log(e.scale) / Math.LN2 + zoom - map.zoom(), p2, l2); if (rotate) map.angle(e.rotation / 180 * Math.PI + angle); e.preventDefault(); break; } } } touch.rotate = function(x) { if (!arguments.length) return rotate; rotate = x; return touch; }; touch.map = function(x) { if (!arguments.length) return map; if (map) { container.removeEventListener("touchstart", touchstart, false); window.removeEventListener("touchmove", touchmove, false); container = null; } if (map = x) { container = map.container(); container.addEventListener("touchstart", touchstart, false); window.addEventListener("touchmove", touchmove, false); } return touch; }; return touch; }; // Default map controls. po.interact = function() { var interact = {}, drag = po.drag(), wheel = po.wheel(), dblclick = po.dblclick(), touch = po.touch(), arrow = po.arrow(); interact.map = function(x) { drag.map(x); wheel.map(x); dblclick.map(x); touch.map(x); arrow.map(x); return interact; }; return interact; }; po.compass = function() { var compass = {}, g = po.svg("g"), ticks = {}, r = 30, speed = 16, last = 0, repeatDelay = 250, repeatInterval = 50, position = "top-left", // top-left, top-right, bottom-left, bottom-right zoomStyle = "small", // none, small, big zoomContainer, panStyle = "small", // none, small panTimer, panDirection, panContainer, drag, dragRect = po.svg("rect"), map, container, window; g.setAttribute("class", "compass"); dragRect.setAttribute("class", "back fore"); dragRect.setAttribute("pointer-events", "none"); dragRect.setAttribute("display", "none"); function panStart(e) { g.setAttribute("class", "compass active"); if (!panTimer) panTimer = setInterval(panRepeat, repeatInterval); if (panDirection) map.panBy(panDirection); last = Date.now(); return cancel(e); } function panRepeat() { if (panDirection && (Date.now() > last + repeatDelay)) { map.panBy(panDirection); } } function mousedown(e) { if (e.shiftKey) { drag = {x0: map.mouse(e)}; map.focusableParent().focus(); return cancel(e); } } function mousemove(e) { if (!drag) return; drag.x1 = map.mouse(e); dragRect.setAttribute("x", Math.min(drag.x0.x, drag.x1.x)); dragRect.setAttribute("y", Math.min(drag.x0.y, drag.x1.y)); dragRect.setAttribute("width", Math.abs(drag.x0.x - drag.x1.x)); dragRect.setAttribute("height", Math.abs(drag.x0.y - drag.x1.y)); dragRect.removeAttribute("display"); } function mouseup(e) { g.setAttribute("class", "compass"); if (drag) { if (drag.x1) { map.extent([ map.pointLocation({ x: Math.min(drag.x0.x, drag.x1.x), y: Math.max(drag.x0.y, drag.x1.y) }), map.pointLocation({ x: Math.max(drag.x0.x, drag.x1.x), y: Math.min(drag.x0.y, drag.x1.y) }) ]); dragRect.setAttribute("display", "none"); } drag = null; } if (panTimer) { clearInterval(panTimer); panTimer = 0; } } function panBy(x) { return function() { x ? this.setAttribute("class", "active") : this.removeAttribute("class"); panDirection = x; }; } function zoomBy(x) { return function(e) { g.setAttribute("class", "compass active"); var z = map.zoom(); map.zoom(x < 0 ? Math.ceil(z) - 1 : Math.floor(z) + 1); return cancel(e); }; } function zoomTo(x) { return function(e) { map.zoom(x); return cancel(e); }; } function zoomOver() { this.setAttribute("class", "active"); } function zoomOut() { this.removeAttribute("class"); } function cancel(e) { e.stopPropagation(); e.preventDefault(); return false; } function pan(by) { var x = Math.SQRT1_2 * r, y = r * .7, z = r * .2, g = po.svg("g"), dir = g.appendChild(po.svg("path")), chv = g.appendChild(po.svg("path")); dir.setAttribute("class", "direction"); dir.setAttribute("pointer-events", "all"); dir.setAttribute("d", "M0,0L" + x + "," + x + "A" + r + "," + r + " 0 0,1 " + -x + "," + x + "Z"); chv.setAttribute("class", "chevron"); chv.setAttribute("d", "M" + z + "," + (y - z) + "L0," + y + " " + -z + "," + (y - z)); chv.setAttribute("pointer-events", "none"); g.addEventListener("mousedown", panStart, false); g.addEventListener("mouseover", panBy(by), false); g.addEventListener("mouseout", panBy(null), false); g.addEventListener("dblclick", cancel, false); return g; } function zoom(by) { var x = r * .4, y = x / 2, g = po.svg("g"), back = g.appendChild(po.svg("path")), dire = g.appendChild(po.svg("path")), chev = g.appendChild(po.svg("path")), fore = g.appendChild(po.svg("path")); back.setAttribute("class", "back"); back.setAttribute("d", "M" + -x + ",0V" + -x + "A" + x + "," + x + " 0 1,1 " + x + "," + -x + "V0Z"); dire.setAttribute("class", "direction"); dire.setAttribute("d", back.getAttribute("d")); chev.setAttribute("class", "chevron"); chev.setAttribute("d", "M" + -y + "," + -x + "H" + y + (by > 0 ? "M0," + (-x - y) + "V" + -y : "")); fore.setAttribute("class", "fore"); fore.setAttribute("fill", "none"); fore.setAttribute("d", back.getAttribute("d")); g.addEventListener("mousedown", zoomBy(by), false); g.addEventListener("mouseover", zoomOver, false); g.addEventListener("mouseout", zoomOut, false); g.addEventListener("dblclick", cancel, false); return g; } function tick(i) { var x = r * .2, y = r * .4, g = po.svg("g"), back = g.appendChild(po.svg("rect")), chev = g.appendChild(po.svg("path")); back.setAttribute("pointer-events", "all"); back.setAttribute("fill", "none"); back.setAttribute("x", -y); back.setAttribute("y", -.75 * y); back.setAttribute("width", 2 * y); back.setAttribute("height", 1.5 * y); chev.setAttribute("class", "chevron"); chev.setAttribute("d", "M" + -x + ",0H" + x); g.addEventListener("mousedown", zoomTo(i), false); g.addEventListener("dblclick", cancel, false); return g; } function move() { var x = r + 6, y = x, size = map.size(); switch (position) { case "top-left": break; case "top-right": x = size.x - x; break; case "bottom-left": y = size.y - y; break; case "bottom-right": x = size.x - x; y = size.y - y; break; } g.setAttribute("transform", "translate(" + x + "," + y + ")"); dragRect.setAttribute("transform", "translate(" + -x + "," + -y + ")"); for (var i in ticks) { i == map.zoom() ? ticks[i].setAttribute("class", "active") : ticks[i].removeAttribute("class"); } } function draw() { while (g.lastChild) g.removeChild(g.lastChild); g.appendChild(dragRect); if (panStyle != "none") { panContainer = g.appendChild(po.svg("g")); panContainer.setAttribute("class", "pan"); var back = panContainer.appendChild(po.svg("circle")); back.setAttribute("class", "back"); back.setAttribute("r", r); var s = panContainer.appendChild(pan({x: 0, y: -speed})); s.setAttribute("transform", "rotate(0)"); var w = panContainer.appendChild(pan({x: speed, y: 0})); w.setAttribute("transform", "rotate(90)"); var n = panContainer.appendChild(pan({x: 0, y: speed})); n.setAttribute("transform", "rotate(180)"); var e = panContainer.appendChild(pan({x: -speed, y: 0})); e.setAttribute("transform", "rotate(270)"); var fore = panContainer.appendChild(po.svg("circle")); fore.setAttribute("fill", "none"); fore.setAttribute("class", "fore"); fore.setAttribute("r", r); } else { panContainer = null; } if (zoomStyle != "none") { zoomContainer = g.appendChild(po.svg("g")); zoomContainer.setAttribute("class", "zoom"); var j = -.5; if (zoomStyle == "big") { ticks = {}; for (var i = map.zoomRange()[0], j = 0; i <= map.zoomRange()[1]; i++, j++) { (ticks[i] = zoomContainer.appendChild(tick(i))) .setAttribute("transform", "translate(0," + (-(j + .75) * r * .4) + ")"); } } var p = panStyle == "none" ? .4 : 2; zoomContainer.setAttribute("transform", "translate(0," + r * (/^top-/.test(position) ? (p + (j + .5) * .4) : -p) + ")"); zoomContainer.appendChild(zoom(+1)).setAttribute("transform", "translate(0," + (-(j + .5) * r * .4) + ")"); zoomContainer.appendChild(zoom(-1)).setAttribute("transform", "scale(-1)"); } else { zoomContainer = null; } move(); } compass.radius = function(x) { if (!arguments.length) return r; r = x; if (map) draw(); return compass; }; compass.speed = function(x) { if (!arguments.length) return r; speed = x; return compass; }; compass.position = function(x) { if (!arguments.length) return position; position = x; if (map) draw(); return compass; }; compass.pan = function(x) { if (!arguments.length) return panStyle; panStyle = x; if (map) draw(); return compass; }; compass.zoom = function(x) { if (!arguments.length) return zoomStyle; zoomStyle = x; if (map) draw(); return compass; }; compass.map = function(x) { if (!arguments.length) return map; if (map) { container.removeEventListener("mousedown", mousedown, false); container.removeChild(g); container = null; window.removeEventListener("mousemove", mousemove, false); window.removeEventListener("mouseup", mouseup, false); window = null; map.off("move", move).off("resize", move); } if (map = x) { container = map.container(); container.appendChild(g); container.addEventListener("mousedown", mousedown, false); window = container.ownerDocument.defaultView; window.addEventListener("mousemove", mousemove, false); window.addEventListener("mouseup", mouseup, false); map.on("move", move).on("resize", move); draw(); } return compass; }; return compass; }; po.grid = function() { var grid = {}, map, g = po.svg("g"); g.setAttribute("class", "grid"); function move(e) { var p, line = g.firstChild, size = map.size(), nw = map.pointLocation(zero), se = map.pointLocation(size), step = Math.pow(2, 4 - Math.round(map.zoom())); // Round to step. nw.lat = Math.floor(nw.lat / step) * step; nw.lon = Math.ceil(nw.lon / step) * step; // Longitude ticks. for (var x; (x = map.locationPoint(nw).x) <= size.x; nw.lon += step) { if (!line) line = g.appendChild(po.svg("line")); line.setAttribute("x1", x); line.setAttribute("x2", x); line.setAttribute("y1", 0); line.setAttribute("y2", size.y); line = line.nextSibling; } // Latitude ticks. for (var y; (y = map.locationPoint(nw).y) <= size.y; nw.lat -= step) { if (!line) line = g.appendChild(po.svg("line")); line.setAttribute("y1", y); line.setAttribute("y2", y); line.setAttribute("x1", 0); line.setAttribute("x2", size.x); line = line.nextSibling; } // Remove extra ticks. while (line) { var next = line.nextSibling; g.removeChild(line); line = next; } } grid.map = function(x) { if (!arguments.length) return map; if (map) { g.parentNode.removeChild(g); map.off("move", move).off("resize", move); } if (map = x) { map.on("move", move).on("resize", move); map.container().appendChild(g); map.dispatch({type: "move"}); } return grid; }; return grid; }; po.stylist = function() { var attrs = [], styles = [], title; function stylist(e) { var ne = e.features.length, na = attrs.length, ns = styles.length, f, // feature d, // data o, // element x, // attr or style or title descriptor v, // attr or style or title value i, j; for (i = 0; i < ne; ++i) { if (!(o = (f = e.features[i]).element)) continue; d = f.data; for (j = 0; j < na; ++j) { v = (x = attrs[j]).value; if (typeof v === "function") v = v.call(null, d); v == null ? (x.name.local ? o.removeAttributeNS(x.name.space, x.name.local) : o.removeAttribute(x.name)) : (x.name.local ? o.setAttributeNS(x.name.space, x.name.local, v) : o.setAttribute(x.name, v)); } for (j = 0; j < ns; ++j) { v = (x = styles[j]).value; if (typeof v === "function") v = v.call(null, d); v == null ? o.style.removeProperty(x.name) : o.style.setProperty(x.name, v, x.priority); } if (v = title) { if (typeof v === "function") v = v.call(null, d); while (o.lastChild) o.removeChild(o.lastChild); if (v != null) o.appendChild(po.svg("title")).appendChild(document.createTextNode(v)); } } } stylist.attr = function(n, v) { attrs.push({name: ns(n), value: v}); return stylist; }; stylist.style = function(n, v, p) { styles.push({name: n, value: v, priority: arguments.length < 3 ? null : p}); return stylist; }; stylist.title = function(v) { title = v; return stylist; }; return stylist; }; })(org.polymaps); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/sphericalmercator.js ================================================ var SphericalMercator = (function(){ // Closures including constants and other precalculated values. var cache = {}, EPSLN = 1.0e-10, D2R = Math.PI / 180, R2D = 180 / Math.PI, // 900913 properties. A = 6378137, MAXEXTENT = 20037508.34; // SphericalMercator constructor: precaches calculations // for fast tile lookups. function SphericalMercator(options) { options = options || {}; this.size = options.size || 256; if (!cache[this.size]) { var size = this.size; var c = cache[this.size] = {}; c.Bc = []; c.Cc = []; c.zc = []; c.Ac = []; for (var d = 0; d < 30; d++) { c.Bc.push(size / 360); c.Cc.push(size / (2 * Math.PI)); c.zc.push(size / 2); c.Ac.push(size); size *= 2; } } this.Bc = cache[this.size].Bc; this.Cc = cache[this.size].Cc; this.zc = cache[this.size].zc; this.Ac = cache[this.size].Ac; }; // Convert lon lat to screen pixel value // // - `ll` {Array} `[lon, lat]` array of geographic coordinates. // - `zoom` {Number} zoom level. SphericalMercator.prototype.px = function(ll, zoom) { var d = this.zc[zoom]; var f = Math.min(Math.max(Math.sin(D2R * ll[1]), -0.9999), 0.9999); var x = Math.round(d + ll[0] * this.Bc[zoom]); var y = Math.round(d + 0.5 * Math.log((1 + f) / (1 - f)) * (-this.Cc[zoom])); (x > this.Ac[zoom]) && (x = this.Ac[zoom]); (y > this.Ac[zoom]) && (y = this.Ac[zoom]); //(x < 0) && (x = 0); //(y < 0) && (y = 0); return [x, y]; }; // Convert screen pixel value to lon lat // // - `px` {Array} `[x, y]` array of geographic coordinates. // - `zoom` {Number} zoom level. SphericalMercator.prototype.ll = function(px, zoom) { var g = (px[1] - this.zc[zoom]) / (-this.Cc[zoom]); var lon = (px[0] - this.zc[zoom]) / this.Bc[zoom]; var lat = R2D * (2 * Math.atan(Math.exp(g)) - 0.5 * Math.PI); return [lon, lat]; }; // Convert tile xyz value to bbox of the form `[w, s, e, n]` // // - `x` {Number} x (longitude) number. // - `y` {Number} y (latitude) number. // - `zoom` {Number} zoom. // - `tms_style` {Boolean} whether to compute using tms-style. // - `srs` {String} projection for resulting bbox (WGS84|900913). // - `return` {Array} bbox array of values in form `[w, s, e, n]`. SphericalMercator.prototype.bbox = function(x, y, zoom, tms_style, srs) { // Convert xyz into bbox with srs WGS84 if (tms_style) { y = (Math.pow(2, zoom) - 1) - y; } // Use +y to make sure it's a number to avoid inadvertent concatenation. var ll = [x * this.size, (+y + 1) * this.size]; // lower left // Use +x to make sure it's a number to avoid inadvertent concatenation. var ur = [(+x + 1) * this.size, y * this.size]; // upper right var bbox = this.ll(ll, zoom).concat(this.ll(ur, zoom)); // If web mercator requested reproject to 900913. if (srs === '900913') { return this.convert(bbox, '900913'); } else { return bbox; } }; // Convert bbox to xyx bounds // // - `bbox` {Number} bbox in the form `[w, s, e, n]`. // - `zoom` {Number} zoom. // - `tms_style` {Boolean} whether to compute using tms-style. // - `srs` {String} projection of input bbox (WGS84|900913). // - `@return` {Object} XYZ bounds containing minX, maxX, minY, maxY properties. SphericalMercator.prototype.xyz = function(bbox, zoom, tms_style, srs) { // If web mercator provided reproject to WGS84. if (srs === '900913') { bbox = this.convert(bbox, 'WGS84'); } var ll = [bbox[0], bbox[1]]; // lower left var ur = [bbox[2], bbox[3]]; // upper right var px_ll = this.px(ll, zoom); var px_ur = this.px(ur, zoom); // Y = 0 for XYZ is the top hence minY uses px_ur[1]. var bounds = { minX: Math.floor(px_ll[0] / this.size), minY: Math.floor(px_ur[1] / this.size), maxX: Math.floor((px_ur[0] - 1) / this.size), maxY: Math.floor((px_ll[1] - 1) / this.size) }; if (tms_style) { var tms = { minY: (Math.pow(2, zoom) - 1) - bounds.maxY, maxY: (Math.pow(2, zoom) - 1) - bounds.minY }; bounds.minY = tms.minY; bounds.maxY = tms.maxY; } return bounds; }; // Convert projection of given bbox. // // - `bbox` {Number} bbox in the form `[w, s, e, n]`. // - `to` {String} projection of output bbox (WGS84|900913). Input bbox // assumed to be the "other" projection. // - `@return` {Object} bbox with reprojected coordinates. SphericalMercator.prototype.convert = function(bbox, to) { if (to === '900913') { return this.forward(bbox.slice(0, 2)).concat(this.forward(bbox.slice(2,4))); } else { return this.inverse(bbox.slice(0, 2)).concat(this.inverse(bbox.slice(2,4))); } }; // Convert lon/lat values to 900913 x/y. SphericalMercator.prototype.forward = function(ll) { var xy = [ A * ll[0] * D2R, A * Math.log(Math.tan((Math.PI*0.25) + (0.5 * ll[1] * D2R))) ]; // if xy value is beyond maxextent (e.g. poles), return maxextent. (xy[0] > MAXEXTENT) && (xy[0] = MAXEXTENT); (xy[0] < -MAXEXTENT) && (xy[0] = -MAXEXTENT); (xy[1] > MAXEXTENT) && (xy[1] = MAXEXTENT); (xy[1] < -MAXEXTENT) && (xy[1] = -MAXEXTENT); return xy; }; // Convert 900913 x/y values to lon/lat. SphericalMercator.prototype.inverse = function(xy) { return [ (xy[0] * R2D / A), ((Math.PI*0.5) - 2.0 * Math.atan(Math.exp(-xy[1] / A))) * R2D ]; }; return SphericalMercator; })(); if (typeof module !== 'undefined' && typeof exports !== 'undefined') { module.exports = exports = SphericalMercator; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arc.js-gh-pages/test.js ================================================ #!/usr/bin/env node var arc = require('./arc.js'); var features = []; var geojson = { 'type': 'FeatureCollection', 'features': features }; var routes = [ [new arc.Coord(-122, 48), new arc.Coord(-77, 39), {'name': 'Seattle to DC'}], [new arc.Coord(-122, 48), new arc.Coord(0, 51), {'name': 'Seattle to London'}], [new arc.Coord(-75.9375,35.460669951495305), new arc.Coord(146.25,-43.06888777416961), {'name': 'crosses dateline'}], [new arc.Coord(145.54687500000003,48.45835188280866 ), new arc.Coord(-112.5,-37.71859032558814), {'name': 'crosses dateline'}] ]; routes.forEach(function(route,idx) { var gc = new arc.GreatCircle(route[0], route[1], route[2]); var line = gc.Arc(50); features.push(line.json()); }); console.log(JSON.stringify(geojson)); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/arcDecorator.js ================================================ var ArcLayer = function(opt) { this.arcStyle = { color : '#fe6f16', thickness : 4, speed : 10 }; this.data = opt.data; this.map = opt.map; L.Draw.customData = new L.LayerGroup(); L.Draw.customData.addTo(this.map); this.addLayer = function() { var context = this; var _map = this.map; var centerLatLng = this.data.centerLatLng; if (this.data.latLng) { var arrowOffset = 0; var arrowArray = []; for ( var j = 0; j < this.data.latLng.length; j++) { var iJson = this.data.latLng[j]; var polyline; var p0 = L.latLng(centerLatLng.lat, centerLatLng.lng); var p1 = L.latLng(iJson.lat, iJson.lng); if (this.data.isOut == '1') { polyline = LMapLib.CurveLine([ p0, p1 ]); } else { polyline = LMapLib.CurveLine([ p1, p0 ]); } var arrowHead = L.polylineDecorator(polyline); var _line = L.polyline(polyline.getLatLngs, { lineCap : 'butt' }) L.Draw.customData.addLayer(polyline); L.Draw.customData.addLayer(arrowHead); arrowArray.push(arrowHead); } // L.symbol.arrowHeader({ // pixelSize : 10, // polygon : true, // pathOptions : { // stroke : true // } // }) var anim = window.setInterval(function() { for ( var i = 0; i < arrowArray.length; i++) { arrowArray[i].setPatterns([ { offset : arrowOffset + '%', repeat : 0, symbol : L.Symbol.marker({ rotate: false, markerOptions: { icon: L.icon({ iconUrl: '../static/lab/gis/images/cz.png', iconAnchor: [8, 8] }) } }) } ]); arrowOffset += 0.5; if (arrowOffset > 100) arrowOffset = 0; } }, 100); } }; this.removeLayer = function() { L.Draw.customData.clearLayers(); }; }; ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/jslib/bar.js ================================================ var BarLayer = function(opt){ this.circleStyle = { stroke : false, fillColor : '#fe6f16', fillOpacity : 0.7 }; this.data = opt.data; this.map = opt.map; L.Draw.customData = new L.LayerGroup(); L.Draw.customData.addTo(this.map); this.addLayer = function(data){ if(!data){ data = this.data; } var context = this; var _map = this.map; for(var i = 0; i < data.length; i++){ var item = data[i]; var style = {}; for(var key in this.circleStyle){ style[key] = this.circleStyle[key]; } if(item.style){ for(var key in item.style){ style[key] = item.style[key]; } } var layer = new L.CircleMarker(item.latlng, style); layer.setRadius(item.radius); layer.data = item; //添加点击事件 if(item.click){ layer.on("click", function(){ //执行单击回调函数 if(this.data.click){ this.data.click(); } }); } //如果存在子元素则附加绑定单击事件 切换到资源富范围 if(item.child){ layer.on("click", function(){ if(this.data.child){ context.removeLayer(); context.addLayer(this.data.child); //跳转到子元素范围 if(this.data.childBbox){ _map.fitBounds(this.data.childBbox); } } }); } //绑定气泡 if(item.popupContent){ var popup = L.popup({ offset : L.point(0, -(item.radius)), closeButton : false }).setLatLng(item.latlng).setContent(item.popupContent); layer.popup = popup; layer.on("mouseover", function(){ _map.openPopup(this.popup); }); layer.on("mouseout", function(){ if(this.popup){ _map.closePopup(this.popup); } }); } //绑定鼠标选中的动画效果 layer.on("mouseover", function(){ this.setStyle({ fillOpacity : parseFloat(this.options.fillOpacity) + 0.2 }); }); layer.on("mouseout", function(){ this.setStyle({ fillOpacity : parseFloat(this.options.fillOpacity) - 0.2 }); this.redraw(); }); L.Draw.customData.addLayer(layer); } } this.removeLayer = function(){ L.Draw.customData.clearLayers(); } } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/test/data/cirlData.js ================================================ var cirlData = [//测试数据 { title : 'beijing', radius : 40,//圆半径 style : {//圆样式 fillColor : 'red' }, latlng : [41.802542590364354, 123.431396484375],//圆中心点 click : function(){ console.log("click bj"); }, popupContent : "气泡内容====",//气泡提示内容 childBbox : [[42.36666166373274, 118.10852050781251],[38.57823196583313, 125.71105957031249 ]],//点击转曲,时设置转曲后的地图范围 child : [ { title : 'beijing', radius : 30, latlng : [41.802542590364354, 123.431396484375], click : function(){ alert(this.radius); }, popupContent : "旗袍内容1" }, { title : 'beijing', latlng : [41.57436130598913, 120.421142578125], radius : 40, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!1" }, { title : 'beijing', latlng : [42.02481360781777, 121.70654296874999], radius : 35, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!2" }, { title : 'beijing', latlng : [41.054501963290505, 121.11328124999999], radius : 24, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!3" }, { title : 'beijing', latlng : [40.6723059714534, 120.838623046875], radius : 30, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!4" }, { title : 'beijing', latlng : [41.09591205639546, 122.05810546875], radius : 24, click : function(){ alert(this.radius); } }, { title : 'beijing', latlng : [38.90920161982435, 121.61590576171876], radius : 60, click : function(){ alert(this.radius); } }, { title : 'beijing', latlng : [41.04621681452063, 123.035888671875], radius : 36, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!3" }, { title : 'beijing', latlng : [41.97780646738183, 123.77197265625], radius : 50, click : function(){ alert(this.radius); }, popupContent : "这里是气泡内容!4" }, ] },{ title : 'beijing', latlng : [34.252676117101515, 108.896484375], radius : 30, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [30.183121842195515, 120.1904296875], radius : 25, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [23.200960808078566, 113.5546875], radius : 40, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [29.611670115197377, 106.5234375], radius : 34, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [35.746512259918504, 96.6796875], radius : 21, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [42.81152174509788, 113.466796875], radius : 29, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [45.767522962149904, 126.474609375], radius : 36, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [40.27952566881291, 86.0009765625], radius : 40, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" },{ title : 'beijing', latlng : [30.93992433102347, 89.033203125], radius : 30, click : function(){ console.log("click xa"); }, popupContent : "气泡内容" } ]; ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/js/test/demo.js ================================================ //加载地图 function onloadMap(){ var mapServerUrl = "http://172.16.247.100:8888/map";//地图服务地址 initMap(mapServerUrl);//初始化地图 minRoad(); } //地图标点 function addMarkers(){ L.Draw.customData = L.layerGroup().addTo(map);//创建标点图层,删除标点时用 var poinListJson = [{id: 1,x: 37.94,y: 112.64,name:'太原'},{id: 2,x: 40.82149932,y: 111.6842769,name:'呼和浩特市'},{id: 3,x: 40.13254611,y: 116.5056088,name:'北京'}];//测试数据 //标注点样式 var myIcon = L.icon({ iconUrl: "images/markers/blue.png", iconSize: [36, 36], popupAnchor: [0, -18] }); for(var i=0;i 100) arrowOffset = 0; }, 100); } function getBox(){ var s = map.getBounds(); alert(s.getWest()); //alert(s.getWest()); } //显示柱状图 function drawBarGraph() { var colors = Highcharts.getOptions().colors; var data = [ { latlng : [ 41.78769700539063, 123.4423828125 ], labelColor : 'red', label : [ '棉花', '小麦', '大豆' ], width : 150, height : 150, clickFun : function(data) { alert(data.y); }, data : [ { y : 55.11, color : colors[0] }, { y : 21.63, color : colors[1] }, { y : 11.94, color : colors[2] } ] }, { latlng : [ 39.436192999314095, 88.06640625 ], label : [ '棉花', '小麦', '大豆' ], labelColor : 'blue', width : 150, height : 150, clickFun : function(data) { alert(data.y + " " + data.category); }, data : [ { y : 55.11, color : colors[0] }, { y : 60, color : colors[1] }, { y : 11.94, color : colors[2] } ] }, { latlng : [ 25.958044673317843, 112.587890625 ], label : [ '棉花', '小麦', '大豆' ], labelColor : 'green', width : 150, height : 150, clickFun : function(data) { alert(data.y); }, data : [ { y : 40, color : colors[0] }, { y : 21.63, color : colors[1] }, { y : 30, color : colors[2] } ] } ]; $("#map").mapColumn({ map : map }); $("#map").mapColumn("addCharts", data); } //删除柱状图 function cleanBarGraph(){ $("#map").mapColumn("clearCharts"); } //最短路径分析 var startMarker = null; var endMarker = null; var flag = ""; //用于描述当前取的点是起点还是终点 function minRoad(){ $("#setStartPointBtn").click(function(){ flag = "s"; }); $("#setEndPointBtn").click(function(){ flag = "e"; }); map.on('click', function(e){ if(flag == "s"){ $("#startPoint").val(e.latlng.lng + " " + e.latlng.lat); if(startMarker){ map.removeLayer(startMarker); } startMarker = L.marker(e.latlng).addTo(map); }else if(flag == "e"){ $("#endPoint").val(e.latlng.lng + " " + e.latlng.lat); if(endMarker){ map.removeLayer(endMarker); } endMarker = L.marker(e.latlng).addTo(map); } console.log(e.latlng.lng + " " + e.latlng.lat); }); $("#getRouting").click(function(){ $.ajax({ url : "routing", type : "post", dataType : 'json', data : { startPoint : $("#startPoint").val(), endPoint : $("#endPoint").val() }, success : function(data){ if(data.msg){ alert(data.msg); return false; } if(routeLayer){ routeLayer.clearLayers(); } for(var i = 0; i < data.length;i++){ var layer = routeLayer.addLayer(L.geoJson(eval('(' + data[i] + ')')).bindPopup("这里是线路信息")); } }, error : function(e){ alert("内部错误!"); } }); }); } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/leaflet/L.Control.Zoominfo.css ================================================ /** Slider **/ .leaflet-control-zoominfo-wrap { padding-top: 5px; padding-bottom: 5px; background-color: #fff; border-bottom: 1px solid #ccc; } .leaflet-control-zoominfo-body { width: 2px; border: solid #fff; border-width: 0px 9px 0px 9px; background-color: black; margin: 0 auto; } .leaflet-control-zoominfo-body:hover { cursor: pointer; } .leaflet-dragging .leaflet-control-zoominfo, .leaflet-dragging .leaflet-control-zoominfo-wrap, .leaflet-dragging .leaflet-control-zoominfo-body, .leaflet-dragging .leaflet-control-zoominfo a, .leaflet-dragging .leaflet-control-zoominfo a.leaflet-control-zoominfo-disabled { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; } /** Leaflet Zoom Styles **/ .leaflet-container .leaflet-control-zoominfo { margin-left: 10px; margin-top: 10px; } .leaflet-control-zoominfo a { width: 26px; height: 26px; text-align: center; text-decoration: none; color: black; display: block; } .leaflet-control-zoominfo a:hover { background-color: #f4f4f4; } .leaflet-control-zoominfo-in { font: bold 18px 'Lucida Console', Monaco, monospace; } .leaflet-control-zoominfo-in:after{ content:"+" } .leaflet-control-zoominfo-out { font: bold 22px 'Lucida Console', Monaco, monospace; } .leaflet-control-zoominfo-out:after{ content:"−" } .leaflet-control-zoominfo a.leaflet-control-zoominfo-disabled { cursor: default; color: #bbb; } /* Touch */ .leaflet-touch .leaflet-control-zoominfo-body { background-position: 10px 0px; } .leaflet-touch .leaflet-control-zoominfo a { width: 30px; line-height: 30px; } .leaflet-touch .leaflet-control-zoominfo a:hover { width: 30px; line-height: 30px; } .leaflet-touch .leaflet-control-zoominfo-in { font-size: 24px; line-height: 29px; } .leaflet-touch .leaflet-control-zoominfo-out { font-size: 28px; line-height: 30px; } .leaflet-touch .leaflet-control-zoominfo { box-shadow: none; border: 4px solid rgba(0,0,0,0.3); } .leaflet-control-zoominfo-info { margin-left: -13px; background-color: #fff; border: none; width: 22px; height: 22px; display: block; text-align: center; text-decoration: none; color: black; padding: 4px 4px 0px 3px; border-radius: 4px; cursor: default; } .leaflet-control-zoominfoinfo h4 { margin: 0 0 0px 0px; color: #000; } /* Old IE */ .leaflet-oldie .leaflet-control-zoominfo-wrap { width: 26px; } .leaflet-oldie .leaflet-control-zoominfo { border: 1px solid #999; } .leaflet-oldie .leaflet-control-zoominfo-in { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '+'); } .leaflet-oldie .leaflet-control-zoominfo-out { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '-'); } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/leaflet/L.Control.Zoominfo.js ================================================ (function (factory) { // Packaging/modules magic dance var L; if (typeof define === 'function' && define.amd) { // AMD define(['leaflet/leaflet'], factory); } else if (typeof module !== 'undefined') { // Node/CommonJS L = require('leaflet/leaflet'); module.exports = factory(L); } else { // Browser globals if (typeof window.L === 'undefined') { throw new Error('Leaflet must be loaded first'); } factory(window.L); } }(function (L) { 'use strict'; L.Control.Zoominfo = (function () { var Zoominfo = L.Control.extend({ options: { position: 'topleft', styleNS: 'leaflet-control-zoominfo' }, onAdd: function (map) { this._map = map; this._ui = this._createUI(); map.whenReady(this._initInfo, this) .whenReady(this._initEvents, this) .whenReady(this._updateInfoValue, this) .whenReady(this._updateDisabled, this); return this._ui.bar; }, onRemove: function (map) { map.off('zoomlevelschange', this._updateSize, this) .off('zoomend zoomlevelschange', this._updateInfoValue, this) .off('zoomend zoomlevelschange', this._updateDisabled, this); }, _createUI: function () { var ui = {}, ns = this.options.styleNS; ui.bar = L.DomUtil.create('div', ns + ' leaflet-bar'); ui.zoomIn = this._createZoomBtn('in', 'top', ui.bar); ui.wrap = L.DomUtil.create('div', ns + '-wrap leaflet-bar-part', ui.bar); ui.zoomOut = this._createZoomBtn('out', 'bottom', ui.bar); ui.body = L.DomUtil.create('div', ns + '-body', ui.wrap); ui.info = L.DomUtil.create('div', ns + '-info'); L.DomEvent.disableClickPropagation(ui.bar); L.DomEvent.disableClickPropagation(ui.info); return ui; }, _createZoomBtn: function (zoomDir, end, container) { var classDef = this.options.styleNS + '-' + zoomDir + ' leaflet-bar-part' + ' leaflet-bar-part-' + end, link = L.DomUtil.create('a', classDef, container); link.href = '#'; link.title = 'Zoom ' + zoomDir; L.DomEvent.on(link, 'click', L.DomEvent.preventDefault); return link; }, _initInfo: function () { this._ui.body.appendChild(this._ui.info); }, _initEvents: function () { this._map .on('zoomend zoomlevelschange', this._updateInfoValue, this) .on('zoomend zoomlevelschange', this._updateDisabled, this); L.DomEvent.on(this._ui.zoomIn, 'click', this._zoomIn, this); L.DomEvent.on(this._ui.zoomOut, 'click', this._zoomOut, this); }, _zoomIn: function (e) { this._map.zoomIn(e.shiftKey ? 3 : 1); }, _zoomOut: function (e) { this._map.zoomOut(e.shiftKey ? 3 : 1); }, _zoomLevels: function () { var zoomLevels = this._map.getMaxZoom() - this._map.getMinZoom() + 1; return zoomLevels < Infinity ? zoomLevels : 0; }, _toZoomLevel: function (value) { return value + this._map.getMinZoom(); }, _toValue: function (zoomLevel) { return zoomLevel - this._map.getMinZoom(); }, _updateInfoValue: function () { this._ui.info.innerHTML = ''+ this._map.getZoom() + ''; }, _updateDisabled: function () { var zoomLevel = this._map.getZoom(), className = this.options.styleNS + '-disabled'; L.DomUtil.removeClass(this._ui.zoomIn, className); L.DomUtil.removeClass(this._ui.zoomOut, className); if (zoomLevel === this._map.getMinZoom()) { L.DomUtil.addClass(this._ui.zoomOut, className); } if (zoomLevel === this._map.getMaxZoom()) { L.DomUtil.addClass(this._ui.zoomIn, className); } } }); return Zoominfo; })(); L.Map.addInitHook(function () { if (this.options.zoominfoControl) { this.zoominfoControl = new L.Control.Zoominfo(); this.addControl(this.zoominfoControl); } }); L.control.zoominfo = function (options) { return new L.Control.Zoominfo(options); }; })); ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/leaflet/leaflet.css ================================================ /* required styles */ .leaflet-pane, .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-tile-container, .leaflet-pane > svg, .leaflet-pane > canvas, .leaflet-zoom-box, .leaflet-image-layer, .leaflet-layer { position: absolute; left: 0; top: 0; } .leaflet-container { overflow: hidden; } .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow { -webkit-user-select: none; -moz-user-select: none; user-select: none; -webkit-user-drag: none; } /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ .leaflet-safari .leaflet-tile { image-rendering: -webkit-optimize-contrast; } /* hack that prevents hw layers "stretching" when loading new tiles */ .leaflet-safari .leaflet-tile-container { width: 1600px; height: 1600px; -webkit-transform-origin: 0 0; } .leaflet-marker-icon, .leaflet-marker-shadow { display: block; } /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ .leaflet-container .leaflet-overlay-pane svg, .leaflet-container .leaflet-marker-pane img, .leaflet-container .leaflet-shadow-pane img, .leaflet-container .leaflet-tile-pane img, .leaflet-container img.leaflet-image-layer { max-width: none !important; max-height: none !important; } .leaflet-container.leaflet-touch-zoom { -ms-touch-action: pan-x pan-y; touch-action: pan-x pan-y; } .leaflet-container.leaflet-touch-drag { -ms-touch-action: pinch-zoom; /* Fallback for FF which doesn't support pinch-zoom */ touch-action: none; touch-action: pinch-zoom; } .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { -ms-touch-action: none; touch-action: none; } .leaflet-container { -webkit-tap-highlight-color: transparent; } .leaflet-container a { -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); } .leaflet-tile { filter: inherit; visibility: hidden; } .leaflet-tile-loaded { visibility: inherit; } .leaflet-zoom-box { width: 0; height: 0; -moz-box-sizing: border-box; box-sizing: border-box; z-index: 800; } /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ .leaflet-overlay-pane svg { -moz-user-select: none; } .leaflet-pane { z-index: 400; } .leaflet-tile-pane { z-index: 200; } .leaflet-overlay-pane { z-index: 400; } .leaflet-shadow-pane { z-index: 500; } .leaflet-marker-pane { z-index: 600; } .leaflet-tooltip-pane { z-index: 650; } .leaflet-popup-pane { z-index: 700; } .leaflet-map-pane canvas { z-index: 100; } .leaflet-map-pane svg { z-index: 200; } .leaflet-vml-shape { width: 1px; height: 1px; } .lvml { behavior: url(#default#VML); display: inline-block; position: absolute; } /* control positioning */ .leaflet-control { position: relative; z-index: 800; pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ pointer-events: auto; } .leaflet-top, .leaflet-bottom { position: absolute; z-index: 1000; pointer-events: none; } .leaflet-top { top: 0; } .leaflet-right { right: 0; } .leaflet-bottom { bottom: 0; } .leaflet-left { left: 0; } .leaflet-control { float: left; clear: both; } .leaflet-right .leaflet-control { float: right; } .leaflet-top .leaflet-control { margin-top: 10px; } .leaflet-bottom .leaflet-control { margin-bottom: 10px; } .leaflet-left .leaflet-control { margin-left: 10px; } .leaflet-right .leaflet-control { margin-right: 10px; } /* zoom and fade animations */ .leaflet-fade-anim .leaflet-tile { will-change: opacity; } .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; -moz-transition: opacity 0.2s linear; -o-transition: opacity 0.2s linear; transition: opacity 0.2s linear; } .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } .leaflet-zoom-animated { -webkit-transform-origin: 0 0; -ms-transform-origin: 0 0; transform-origin: 0 0; } .leaflet-zoom-anim .leaflet-zoom-animated { will-change: transform; } .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); transition: transform 0.25s cubic-bezier(0,0,0.25,1); } .leaflet-zoom-anim .leaflet-tile, .leaflet-pan-anim .leaflet-tile { -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; } .leaflet-zoom-anim .leaflet-zoom-hide { visibility: hidden; } /* cursors */ .leaflet-interactive { cursor: pointer; } .leaflet-grab { cursor: -webkit-grab; cursor: -moz-grab; } .leaflet-crosshair, .leaflet-crosshair .leaflet-interactive { cursor: crosshair; } .leaflet-popup-pane, .leaflet-control { cursor: auto; } .leaflet-dragging .leaflet-grab, .leaflet-dragging .leaflet-grab .leaflet-interactive, .leaflet-dragging .leaflet-marker-draggable { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; } /* marker & overlays interactivity */ .leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-image-layer, .leaflet-pane > svg path, .leaflet-tile-container { pointer-events: none; } .leaflet-marker-icon.leaflet-interactive, .leaflet-image-layer.leaflet-interactive, .leaflet-pane > svg path.leaflet-interactive { pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ pointer-events: auto; } /* visual tweaks */ .leaflet-container { background: #ddd; outline: 0; } .leaflet-container a { color: #0078A8; } .leaflet-container a.leaflet-active { outline: 2px solid orange; } .leaflet-zoom-box { border: 2px dotted #38f; background: rgba(255,255,255,0.5); } /* general typography */ .leaflet-container { font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; } /* general toolbar styles */ .leaflet-bar { box-shadow: 0 1px 5px rgba(0,0,0,0.65); border-radius: 4px; } .leaflet-bar a, .leaflet-bar a:hover { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; height: 26px; line-height: 26px; display: block; text-align: center; text-decoration: none; color: black; } .leaflet-bar a, .leaflet-control-layers-toggle { background-position: 50% 50%; background-repeat: no-repeat; display: block; } .leaflet-bar a:hover { background-color: #f4f4f4; } .leaflet-bar a:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .leaflet-bar a:last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-bottom: none; } .leaflet-bar a.leaflet-disabled { cursor: default; background-color: #f4f4f4; color: #bbb; } .leaflet-touch .leaflet-bar a { width: 30px; height: 30px; line-height: 30px; } .leaflet-touch .leaflet-bar a:first-child { border-top-left-radius: 2px; border-top-right-radius: 2px; } .leaflet-touch .leaflet-bar a:last-child { border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; } /* zoom control */ .leaflet-control-zoom-in, .leaflet-control-zoom-out { font: bold 18px 'Lucida Console', Monaco, monospace; text-indent: 1px; } .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { font-size: 22px; } /* layers control */ .leaflet-control-layers { box-shadow: 0 1px 5px rgba(0,0,0,0.4); background: #fff; border-radius: 5px; } .leaflet-control-layers-toggle { background-image: url(../images/layers.png); width: 36px; height: 36px; } .leaflet-retina .leaflet-control-layers-toggle { background-image: url(../images/layers-2x.png); background-size: 26px 26px; } .leaflet-touch .leaflet-control-layers-toggle { width: 44px; height: 44px; } .leaflet-control-layers .leaflet-control-layers-list, .leaflet-control-layers-expanded .leaflet-control-layers-toggle { display: none; } .leaflet-control-layers-expanded .leaflet-control-layers-list { display: block; position: relative; } .leaflet-control-layers-expanded { padding: 6px 10px 6px 6px; color: #333; background: #fff; } .leaflet-control-layers-scrollbar { overflow-y: scroll; overflow-x: hidden; padding-right: 5px; } .leaflet-control-layers-selector { margin-top: 2px; position: relative; top: 1px; } .leaflet-control-layers label { display: block; } .leaflet-control-layers-separator { height: 0; border-top: 1px solid #ddd; margin: 5px -10px 5px -6px; } /* Default icon URLs */ .leaflet-default-icon-path { background-image: url(../images/marker-icon.png); } /* attribution and scale controls */ .leaflet-container .leaflet-control-attribution { background: #fff; background: rgba(255, 255, 255, 0.7); margin: 0; } .leaflet-control-attribution, .leaflet-control-scale-line { padding: 0 5px; color: #333; } .leaflet-control-attribution a { text-decoration: none; } .leaflet-control-attribution a:hover { text-decoration: underline; } .leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale { font-size: 11px; } .leaflet-left .leaflet-control-scale { margin-left: 5px; } .leaflet-bottom .leaflet-control-scale { margin-bottom: 5px; } .leaflet-control-scale-line { border: 2px solid #777; border-top: none; line-height: 1.1; padding: 2px 5px 1px; font-size: 11px; white-space: nowrap; overflow: hidden; -moz-box-sizing: border-box; box-sizing: border-box; background: #fff; background: rgba(255, 255, 255, 0.5); } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; } .leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; } .leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { box-shadow: none; } .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { border: 2px solid rgba(0,0,0,0.2); background-clip: padding-box; } /* popup */ .leaflet-popup { position: absolute; text-align: center; margin-bottom: 20px; } .leaflet-popup-content-wrapper { padding: 1px; text-align: left; border-radius: 12px; } .leaflet-popup-content { margin: 13px 19px; line-height: 1.4; } .leaflet-popup-content p { margin: 18px 0; } .leaflet-popup-tip-container { width: 40px; height: 20px; position: absolute; left: 50%; margin-left: -20px; overflow: hidden; pointer-events: none; } .leaflet-popup-tip { width: 17px; height: 17px; padding: 1px; margin: -10px auto 0; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg); } .leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white; color: #333; box-shadow: 0 3px 14px rgba(0,0,0,0.4); } .leaflet-container a.leaflet-popup-close-button { position: absolute; top: 0; right: 0; padding: 4px 4px 0 0; border: none; text-align: center; width: 18px; height: 14px; font: 16px/14px Tahoma, Verdana, sans-serif; color: #c3c3c3; text-decoration: none; font-weight: bold; background: transparent; } .leaflet-container a.leaflet-popup-close-button:hover { color: #999; } .leaflet-popup-scrolled { overflow: auto; border-bottom: 1px solid #ddd; border-top: 1px solid #ddd; } .leaflet-oldie .leaflet-popup-content-wrapper { zoom: 1; } .leaflet-oldie .leaflet-popup-tip { width: 24px; margin: 0 auto; -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); } .leaflet-oldie .leaflet-popup-tip-container { margin-top: -1px; } .leaflet-oldie .leaflet-control-zoom, .leaflet-oldie .leaflet-control-layers, .leaflet-oldie .leaflet-popup-content-wrapper, .leaflet-oldie .leaflet-popup-tip { border: 1px solid #999; } /* div icon */ .leaflet-div-icon { background: #fff; border: 1px solid #666; } /* Tooltip */ /* Base styles for the element that has a tooltip */ .leaflet-tooltip { position: absolute; padding: 6px; background-color: #fff; border: 1px solid #fff; border-radius: 3px; color: #222; white-space: nowrap; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; pointer-events: none; box-shadow: 0 1px 3px rgba(0,0,0,0.4); } .leaflet-tooltip.leaflet-clickable { cursor: pointer; pointer-events: auto; } .leaflet-tooltip-top:before, .leaflet-tooltip-bottom:before, .leaflet-tooltip-left:before, .leaflet-tooltip-right:before { position: absolute; pointer-events: none; border: 6px solid transparent; background: transparent; content: ""; } /* Directions */ .leaflet-tooltip-bottom { margin-top: 6px; } .leaflet-tooltip-top { margin-top: -6px; } .leaflet-tooltip-bottom:before, .leaflet-tooltip-top:before { left: 50%; margin-left: -6px; } .leaflet-tooltip-top:before { bottom: 0; margin-bottom: -12px; border-top-color: #fff; } .leaflet-tooltip-bottom:before { top: 0; margin-top: -12px; margin-left: -6px; border-bottom-color: #fff; } .leaflet-tooltip-left { margin-left: -6px; } .leaflet-tooltip-right { margin-left: 6px; } .leaflet-tooltip-left:before, .leaflet-tooltip-right:before { top: 50%; margin-top: -6px; } .leaflet-tooltip-left:before { right: 0; margin-right: -12px; border-left-color: #fff; } .leaflet-tooltip-right:before { left: 0; margin-left: -12px; border-right-color: #fff; } ================================================ FILE: material-manage/src/main/webapp/static/lab/gis/leaflet/leaflet.js ================================================ /* @preserve * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e=0}function I(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function B(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(Qi,n,!1):"touchmove"===i?t.removeEventListener(te,n,!1):"touchend"===i&&(t.removeEventListener(ie,n,!1),t.removeEventListener(ee,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(ne.indexOf(t.target.tagName)<0))return;$(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(Qi,o,!1),se||(document.documentElement.addEventListener(Qi,R,!0),document.documentElement.addEventListener(te,D,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,N,!0),se=!0)}function R(t){oe[t.pointerId]=t,re++}function D(t){oe[t.pointerId]&&(oe[t.pointerId]=t)}function N(t){delete oe[t.pointerId],re--}function j(t,i){t.touches=[];for(var e in oe)t.touches.push(oe[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(te,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ie,n,!1),t.addEventListener(ee,n,!1)}function F(t,i,e){function n(t){var i;if(Ui){if(!Pi||"mouse"===t.pointerType)return;i=re}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Ui){if(!Pi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[ue+ae+e]=n,t[ue+he+e]=o,t[ue+"dblclick"+e]=i,t.addEventListener(ae,n,!1),t.addEventListener(he,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[ue+ae+i],n=t[ue+he+i],o=t[ue+"dblclick"+i];return t.removeEventListener(ae,e,!1),t.removeEventListener(he,n,!1),Pi||t.removeEventListener("dblclick",o,!1),this}function V(t,i,e,n){if("object"==typeof i)for(var o in i)G(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s100&&n<500||t.target._simulatedClick&&!t._simulated?Q(t):(pi=e,i(t))}function rt(t){return"string"==typeof t?document.getElementById(t):t}function at(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function ht(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ut(t){var i=t.parentNode;i&&i.removeChild(t)}function lt(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ct(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function _t(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function dt(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=gt(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function pt(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;nh&&(s=r,h=a);h>e&&(i[s]=1,Et(t,i,e,n,s),Et(t,i,e,s,o))}function kt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;ni&&(e.push(t[n]),o=n);return oi.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function Ot(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Rt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function Dt(t){return!ei(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Nt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Dt(t)}function jt(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=li(this.x),this.y=li(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.xi.y&&n.y=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lati.lng&&n.lng1,Yi=!!document.createElement("canvas").getContext,Xi=!(!document.createElementNS||!E("svg").createSVGRect),Ji=!Xi&&function(){try{var t=document.createElement("div");t.innerHTML='';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),$i=(Object.freeze||Object)({ie:wi,ielt9:Li,edge:Pi,webkit:bi,android:Ti,android23:zi,androidStock:Ci,opera:Zi,chrome:Si,gecko:Ei,safari:ki,phantom:Ai,opera12:Ii,win:Bi,ie3d:Oi,webkit3d:Ri,gecko3d:Di,any3d:Ni,mobile:ji,mobileWebkit:Wi,mobileWebkit3d:Hi,msPointer:Fi,pointer:Ui,touch:Vi,mobileOpera:qi,mobileGecko:Gi,retina:Ki,canvas:Yi,svg:Xi,vml:Ji}),Qi=Fi?"MSPointerDown":"pointerdown",te=Fi?"MSPointerMove":"pointermove",ie=Fi?"MSPointerUp":"pointerup",ee=Fi?"MSPointerCancel":"pointercancel",ne=["INPUT","SELECT","OPTION"],oe={},se=!1,re=0,ae=Fi?"MSPointerDown":Ui?"pointerdown":"touchstart",he=Fi?"MSPointerUp":Ui?"pointerup":"touchend",ue="_leaflet_",le="_leaflet_events",ce=Bi&&Si?2*window.devicePixelRatio:Ei?window.devicePixelRatio:1,_e={},de=(Object.freeze||Object)({on:V,off:q,stopPropagation:Y,disableScrollPropagation:X,disableClickPropagation:J,preventDefault:$,stop:Q,getMousePosition:tt,getWheelDelta:it,fakeStop:et,skipped:nt,isExternalTarget:ot,addListener:V,removeListener:q}),pe=xt(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),me=xt(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),fe="webkitTransition"===me||"OTransition"===me?me+"End":"transitionend";if("onselectstart"in document)mi=function(){V(window,"selectstart",$)},fi=function(){q(window,"selectstart",$)};else{var ge=xt(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);mi=function(){if(ge){var t=document.documentElement.style;gi=t[ge],t[ge]="none"}},fi=function(){ge&&(document.documentElement.style[ge]=gi,gi=void 0)}}var ve,ye,xe=(Object.freeze||Object)({TRANSFORM:pe,TRANSITION:me,TRANSITION_END:fe,get:rt,getStyle:at,create:ht,remove:ut,empty:lt,toFront:ct,toBack:_t,hasClass:dt,addClass:pt,removeClass:mt,setClass:ft,getClass:gt,setOpacity:vt,testProp:xt,setTransform:wt,setPosition:Lt,getPosition:Pt,disableTextSelection:mi,enableTextSelection:fi,disableImageDrag:bt,enableImageDrag:Tt,preventOutline:zt,restoreOutline:Mt}),we=ui.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pt(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;ithis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),ut(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)ut(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=ht("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=Ni?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return tt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=rt(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");V(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ni,pt(t,"leaflet-container"+(Vi?" leaflet-touch":"")+(Ki?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(ki?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=at(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Lt(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(pt(t.markerPane,"leaflet-zoom-hide"),pt(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){Lt(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Lt(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?q:V;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Ni&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!ot(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!ot(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!nt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||zt(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&$(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=Ni?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){mt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=ht("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=pe,e=this._proxy.style[i];wt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();wt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){ut(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pt(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&mt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Pe=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pt(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(ut(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),be=function(t){return new Pe(t)};Le.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=ht("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=ht("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)ut(this._controlCorners[t]);ut(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Te=Pe.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),V(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&ni.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ze=Pe.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ht("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ht("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),J(s),V(s,"click",Q),V(s,"click",o,this),V(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mt(this._zoomInButton,i),mt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&pt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&pt(this._zoomInButton,i)}});Le.mergeOptions({zoomControl:!0}),Le.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ze,this.addControl(this.zoomControl))});var Me=Pe.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=ht("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ht("div",i,e)),t.imperial&&(this._iScale=ht("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ce=Pe.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=ht("div","leaflet-control-attribution"),J(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});Le.mergeOptions({attributionControl:!0}),Le.addInitHook(function(){this.options.attributionControl&&(new Ce).addTo(this)});Pe.Layers=Te,Pe.Zoom=ze,Pe.Scale=Me,Pe.Attribution=Ce,be.layers=function(t,i,e){return new Te(t,i,e)},be.zoom=function(t){return new ze(t)},be.scale=function(t){return new Me(t)},be.attribution=function(t){return new Ce(t)};var Ze=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ze.addTo=function(t,i){return t.addHandler(i,this),this};var Se,Ee={Events:hi},ke=Vi?"touchstart mousedown":"mousedown",Ae={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Ie={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Be=ui.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(V(this._dragStartTarget,ke,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Be._dragging===this&&this.finishDrag(),q(this._dragStartTarget,ke,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!dt(this._element,"leaflet-zoom-anim")&&!(Be._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Be._dragging=this,this._preventOutline&&zt(this._element),bt(),mi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new x(i.clientX,i.clientY),V(document,Ie[t.type],this._onMove,this),V(document,Ae[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY).subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},je=(Object.freeze||Object)({LonLat:De,Mercator:Ne,SphericalMercator:di}),We=i({},_i,{code:"EPSG:3395",projection:Ne,transformation:function(){var t=.5/(Math.PI*Ne.R);return S(t,.5,-t,.5)}()}),He=i({},_i,{code:"EPSG:4326",projection:De,transformation:S(1/180,1,-1/180,.5)}),Fe=i({},ci,{projection:De,transformation:S(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});ci.Earth=_i,ci.EPSG3395=We,ci.EPSG3857=vi,ci.EPSG900913=yi,ci.EPSG4326=He,ci.Simple=Fe;var Ue=ui.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});Le.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?ei(t)?t:[t]:[]).length;ithis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return Dt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=Dt(t),n=0,o=t.length;n=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){tn.prototype._setLatLngs.call(this,t),Dt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Dt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;ot.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||tn.prototype._containsPoint.call(this,t,!0)}}),nn=qe.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=ei(t)?t:t.features;if(o){for(i=0,e=o.length;i0?o:[i.src]}else{ei(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;ao?(i.height=o+"px",pt(t,"leaflet-popup-scrolled")):mt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Lt(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(at(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(Pt(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Q(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Le.mergeOptions({closePopupOnClick:!0}),Le.include({openPopup:function(t,i,e){return t instanceof un||(t=new un(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Ue.include({bindPopup:function(t,i){return t instanceof un?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new un(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Q(t),i instanceof Je?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var ln=hn.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){hn.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=hn.prototype.getEvents.call(this);return Vi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ht("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.xthis.options.maxZoom||en&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1this.options.maxZoom||void 0!==this.options.minZoom&&o1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_e.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(Ci||i.el.setAttribute("src",ni),ut(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pt(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&vt(t,this.options.opacity),Ti&&!zi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),Lt(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){if(this._map){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(vt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(pt(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),dn=_n.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Ki&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),Ti||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return V(n,"load",e(this._tileOnLoad,this,i,n)),V(n,"error",e(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Ki?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=ni,ut(i),delete this._tiles[t]))}}),pn=dn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Ki?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,dn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===He?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=L.TileLayer.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});dn.WMS=pn,Yt.wms=function(t,i){return new pn(t,i)};var mn=Ue.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&pt(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=Pt(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);Ni?wt(this._container,a,e):Lt(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),fn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){mn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");V(t,"mousemove",o(this._onMouseMove,32,this),this),V(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),V(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){delete this._ctx,ut(this._container),q(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},mn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Ki?2:1;Lt(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Ki&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){mn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,n=i.prev;e?e.prev=n:this._drawLast=n,n?n.next=e:this._drawFirst=e,delete t._order,delete this._layers[L.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var i,e=t.options.dashArray.split(","),n=[];for(i=0;i')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),vn={_initContainer:function(){this._container=ht("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(mn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=gn("shape");pt(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=gn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ut(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=gn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=ei(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=gn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ct(t._container)},_bringToBack:function(t){_t(t._container)}},yn=Ji?gn:E,xn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=yn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=yn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ut(this._container),q(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){mn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Lt(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=yn("path");t.options.className&&pt(i,t.options.className),t.options.interactive&&pt(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ut(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ct(t._path)},_bringToBack:function(t){_t(t._path)}});Ji&&xn.include(vn),Le.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this.options.preferCanvas&&Xt()||Jt()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=xn&&Jt({pane:t})||fn&&Xt({pane:t}),this._paneRenderers[t]=i),i}});var wn=en.extend({initialize:function(t,i){en.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});xn.create=yn,xn.pointsToPath=k,nn.geometryToLayer=Wt,nn.coordsToLatLng=Ht,nn.coordsToLatLngs=Ft,nn.latLngToCoords=Ut,nn.latLngsToCoords=Vt,nn.getFeature=qt,nn.asFeature=Gt,Le.mergeOptions({boxZoom:!0});var Ln=Ze.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){V(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){q(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ut(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),mi(),bt(),this._startPoint=this._map.mouseEventToContainerPoint(t),V(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ht("div","leaflet-zoom-box",this._container),pt(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();Lt(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ut(this._box),mt(this._container,"leaflet-crosshair")),fi(),Tt(),q(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});Le.addInitHook("addHandler","boxZoom",Ln),Le.mergeOptions({doubleClickZoom:!0});var Pn=Ze.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});Le.addInitHook("addHandler","doubleClickZoom",Pn),Le.mergeOptions({dragging:!0,inertia:!zi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var bn=Ze.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Be(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pt(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mt(this._map._container,"leaflet-grab"),mt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.xi.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});Le.addInitHook("addHandler","scrollWheelZoom",zn),Le.mergeOptions({tap:!0,tapTolerance:15});var Mn=Ze.extend({addHooks:function(){V(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){q(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if($(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&pt(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),V(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),q(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&mt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});Vi&&!Ui&&Le.addInitHook("addHandler","tap",Mn),Le.mergeOptions({touchZoom:Vi&&!zi,bounceAtZoomLimits:!0});var Cn=Ze.extend({addHooks:function(){pt(this._map._container,"leaflet-touch-zoom"),V(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){mt(this._map._container,"leaflet-touch-zoom"),q(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),V(document,"touchmove",this._onTouchMove,this),V(document,"touchend",this._onTouchEnd,this),$(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoomi.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),$(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),q(document,"touchmove",this._onTouchMove),q(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});Le.addInitHook("addHandler","touchZoom",Cn),Le.BoxZoom=Ln,Le.DoubleClickZoom=Pn,Le.Drag=bn,Le.Keyboard=Tn,Le.ScrollWheelZoom=zn,Le.Tap=Mn,Le.TouchZoom=Cn;var Zn=window.L;window.L=t,Object.freeze=$t,t.version="1.3.1+HEAD.ba6f97f",t.noConflict=function(){return window.L=Zn,this},t.Control=Pe,t.control=be,t.Browser=$i,t.Evented=ui,t.Mixin=Ee,t.Util=ai,t.Class=v,t.Handler=Ze,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=de,t.DomUtil=xe,t.PosAnimation=we,t.Draggable=Be,t.LineUtil=Oe,t.PolyUtil=Re,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=Z,t.transformation=S,t.Projection=je,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=ci,t.GeoJSON=nn,t.geoJSON=Kt,t.geoJson=sn,t.Layer=Ue,t.LayerGroup=Ve,t.layerGroup=function(t,i){return new Ve(t,i)},t.FeatureGroup=qe,t.featureGroup=function(t){return new qe(t)},t.ImageOverlay=rn,t.imageOverlay=function(t,i,e){return new rn(t,i,e)},t.VideoOverlay=an,t.videoOverlay=function(t,i,e){return new an(t,i,e)},t.DivOverlay=hn,t.Popup=un,t.popup=function(t,i){return new un(t,i)},t.Tooltip=ln,t.tooltip=function(t,i){return new ln(t,i)},t.Icon=Ge,t.icon=function(t){return new Ge(t)},t.DivIcon=cn,t.divIcon=function(t){return new cn(t)},t.Marker=Xe,t.marker=function(t,i){return new Xe(t,i)},t.TileLayer=dn,t.tileLayer=Yt,t.GridLayer=_n,t.gridLayer=function(t){return new _n(t)},t.SVG=xn,t.svg=Jt,t.Renderer=mn,t.Canvas=fn,t.canvas=Xt,t.Path=Je,t.CircleMarker=$e,t.circleMarker=function(t,i){return new $e(t,i)},t.Circle=Qe,t.circle=function(t,i,e){return new Qe(t,i,e)},t.Polyline=tn,t.polyline=function(t,i){return new tn(t,i)},t.Polygon=en,t.polygon=function(t,i){return new en(t,i)},t.Rectangle=wn,t.rectangle=function(t,i){return new wn(t,i)},t.Map=Le,t.map=function(t,i){return new Le(t,i)}}); ================================================ FILE: material-manage/src/main/webapp/static/less/app.less ================================================ /* * Load Main Bootstrap LESS files */ @import 'vendors/bower_components/bootstrap/less/bootstrap'; /* * LESS Plugins */ @import 'inc/less-plugins/for'; /* * Variable and Mixin */ @import 'inc/variables'; @import 'inc/mixin'; /* * Load Font */ @import 'inc/font'; /* * Vendors */ @import 'vendors/weather-icons/weather-icons'; @import 'vendors/fileinput/fileinput'; @import 'vendors/bower_components/material-shadows/material-shadows'; @import 'vendors/bower_components/Waves/src/less/waves'; /* * Load Website related LESS files */ @import 'inc/generics'; @import 'inc/bootstrap-overrides'; @import 'inc/base'; @import 'inc/list'; @import 'inc/header'; @import 'inc/sidebar'; @import 'inc/dropdown'; @import 'inc/listview'; @import 'inc/progress-bar'; @import 'inc/chat'; @import 'inc/tabs'; @import 'inc/card'; @import 'inc/chart'; @import 'inc/widgets'; @import 'inc/table'; @import 'inc/todo'; @import 'inc/button'; @import 'inc/form'; @import 'inc/pagination'; @import 'inc/popover'; @import 'inc/wizard'; @import 'inc/alert'; @import 'inc/media'; @import 'inc/modal'; @import 'inc/panel'; @import 'inc/tooltip'; @import 'inc/breadcrumb'; @import 'inc/messages'; @import 'inc/404'; @import 'inc/login'; @import 'inc/profile'; @import 'inc/photos'; @import 'inc/contacts'; @import 'inc/shadow'; @import 'inc/misc'; @import 'inc/ie-warning'; @import 'inc/footer'; @import 'inc/pricing-table'; @import 'inc/invoice'; @import 'inc/wall'; @import 'inc/skin'; @import 'inc/preloader'; @import 'inc/print'; /* * Vendor Overrides */ @import 'inc/vendor-overrides/mediaelement'; @import 'inc/vendor-overrides/fullcalendar'; @import 'inc/vendor-overrides/bootgrid'; @import 'inc/vendor-overrides/bootstrap-select'; @import 'inc/vendor-overrides/chosen'; @import 'inc/vendor-overrides/noUiSlider'; @import 'inc/vendor-overrides/farbtastic'; @import 'inc/vendor-overrides/summernote'; @import 'inc/vendor-overrides/bootstrap-datetimepicker'; @import 'inc/vendor-overrides/fileinput'; @import 'inc/vendor-overrides/light-gallery'; @import 'inc/vendor-overrides/waves'; @import 'inc/vendor-overrides/sweetalert'; @import 'inc/vendor-overrides/typeahead'; @import 'inc/vendor-overrides/malihu-custom-scrollbar'; ================================================ FILE: material-manage/src/main/webapp/static/less/inc/404.less ================================================ .four-zero-content { background: #fff; padding: 20px; &:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #EDECEC; content: ""; } } .four-zero { background: @m-cyan; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; position: absolute; top: 50%; margin-top: -150px; color: #fff; text-align: center; padding: 15px; height: 300px; width: 500px; left: 50%; margin-left: -250px; h2 { font-size: 130px; } @media (max-width: @screen-xs-max) { width: ~"calc(100% - 40px)"; left: 20px; margin-left: 0; height: 260px; margin-top: -130px; h2 { font-size: 90px; } } h2 { line-height: 100%; color: #fff; font-weight: 100; } small { display: block; font-size: 26px; margin-top: -10px } footer { background: rgba(0,0,0,0.13); position: absolute; left: 0; bottom: 0; width: 100%; padding: 10px; & > a { font-size: 21px; display: inline-block; color: #FFF; margin: 0 1px; line-height: 40px; width: 40px; height: 40px; background: rgba(0, 0, 0, 0.09); border-radius: 50%; text-align: center; .transition(all); .transition-duration(300ms); &:hover { background: rgba(0, 0, 0, 0.2); } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/alert.less ================================================ .alert { padding-left: 30px; font-size: 13px; span { cursor: pointer; } &:not(.alert-dismissible) { padding-right: 30px; } &.alert-dismissable { padding-right: 44px; } } .alert-inverse { .alert-variant(@alert-inverse-bg; @alert-inverse-border; @alert-inverse-text); } .growl-animated { &.alert-inverse { box-shadow: 0 0 5px fade(@alert-inverse-bg, 50%); } &.alert-info { box-shadow: 0 0 5px fade(@alert-info-bg, 50%); } &.alert-success { box-shadow: 0 0 5px fade(@alert-success-bg, 50%); } &.alert-warning { box-shadow: 0 0 5px fade(@alert-warning-bg, 50%); } &.alert-danger { box-shadow: 0 0 5px fade(@alert-danger-bg, 50%); } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/base.less ================================================ *, button, input, i, a { -webkit-font-smoothing: antialiased; } *, *:active, *:hover { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } html { overflow-x: ~"hidden\0/"; -ms-overflow-style: none; } html, body { min-height: 100vh; } body { font-weight: 400; position: relative; } audio, video { outline: none; } p { margin-bottom: 20px; } small { font-size: 11px; } h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { small { font-size: 12px; } } #main { position: relative; padding-bottom: @footer-height; padding-top: @header-height + 30; } .container { &.c-alt { max-width: 1170px; } } @media (min-width: @screen-sm-min) and (max-width: @screen-md-max) { #content { padding-left: 15px; padding-right: 15px; } } body { &.sw-toggled { #content { @media (min-width: @screen-lg-min) { padding-left: @sidebar-left-width; } & > .container { @media (min-width: @screen-lg-min) { width: ~"calc(100% - 30px)"; } } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/alerts.less ================================================ // // Alerts // -------------------------------------------------- // Base styles // ------------------------- .alert { padding: @alert-padding; margin-bottom: @line-height-computed; border: 1px solid transparent; border-radius: @alert-border-radius; // Headings for larger alerts h4 { margin-top: 0; // Specified for the h4 to prevent conflicts of changing @headings-color color: inherit; } // Provide class for links that match alerts .alert-link { font-weight: @alert-link-font-weight; } // Improve alignment and spacing of inner content > p, > ul { margin-bottom: 0; } > p + p { margin-top: 5px; } } // Dismissible alerts // // Expand the right padding and account for the close button's positioning. .alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. .alert-dismissible { padding-right: (@alert-padding + 20); // Adjust close link position .close { position: relative; top: -2px; right: -21px; color: inherit; } } // Alternate styles // // Generate contextual modifier classes for colorizing the alert. .alert-success { .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); } .alert-info { .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); } .alert-warning { .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); } .alert-danger { .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/badges.less ================================================ // // Badges // -------------------------------------------------- // Base class .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: @font-size-small; font-weight: @badge-font-weight; color: @badge-color; line-height: @badge-line-height; vertical-align: baseline; white-space: nowrap; text-align: center; background-color: @badge-bg; border-radius: @badge-border-radius; // Empty badges collapse automatically (not available in IE8) &:empty { display: none; } // Quick fix for badges in buttons .btn & { position: relative; top: -1px; } .btn-xs &, .btn-group-xs > .btn & { top: 0; padding: 1px 5px; } // Hover state, but only for links a& { &:hover, &:focus { color: @badge-link-hover-color; text-decoration: none; cursor: pointer; } } // Account for badges in navs .list-group-item.active > &, .nav-pills > .active > a > & { color: @badge-active-color; background-color: @badge-active-bg; } .list-group-item > & { float: right; } .list-group-item > & + & { margin-right: 5px; } .nav-pills > li > a > & { margin-left: 3px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/bootstrap.less ================================================ // Core variables and mixins @import "variables.less"; @import "mixins.less"; // Reset and dependencies @import "normalize.less"; @import "print.less"; @import "glyphicons.less"; // Core CSS @import "scaffolding.less"; @import "type.less"; @import "code.less"; @import "grid.less"; @import "tables.less"; @import "forms.less"; @import "buttons.less"; // Components @import "component-animations.less"; @import "dropdowns.less"; @import "button-groups.less"; @import "input-groups.less"; @import "navs.less"; @import "navbar.less"; @import "breadcrumbs.less"; @import "pagination.less"; @import "pager.less"; @import "labels.less"; @import "badges.less"; @import "jumbotron.less"; @import "thumbnails.less"; @import "alerts.less"; @import "progress-bars.less"; @import "media.less"; @import "list-group.less"; @import "panels.less"; @import "responsive-embed.less"; @import "wells.less"; @import "close.less"; // Components w/ JavaScript @import "modals.less"; @import "tooltip.less"; @import "popovers.less"; @import "carousel.less"; // Utility classes @import "utilities.less"; @import "responsive-utilities.less"; ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/breadcrumbs.less ================================================ // // Breadcrumbs // -------------------------------------------------- .breadcrumb { padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; margin-bottom: @line-height-computed; list-style: none; background-color: @breadcrumb-bg; border-radius: @border-radius-base; > li { display: inline-block; + li:before { content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space padding: 0 5px; color: @breadcrumb-color; } } > .active { color: @breadcrumb-active-color; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/button-groups.less ================================================ // // Button groups // -------------------------------------------------- // Make the div behave like a button .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; // match .btn alignment given font-size hack above > .btn { position: relative; float: left; // Bring the "active" button to the front &:hover, &:focus, &:active, &.active { z-index: 2; } } } // Prevent double borders when buttons are next to each other .btn-group { .btn + .btn, .btn + .btn-group, .btn-group + .btn, .btn-group + .btn-group { margin-left: -1px; } } // Optional: Group multiple button groups together for a toolbar .btn-toolbar { margin-left: -5px; // Offset the first child's margin &:extend(.clearfix all); .btn-group, .input-group { float: left; } > .btn, > .btn-group, > .input-group { margin-left: 5px; } } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match .btn-group > .btn:first-child { margin-left: 0; &:not(:last-child):not(.dropdown-toggle) { .border-right-radius(0); } } // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { .border-left-radius(0); } // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child:not(:last-child) { > .btn:last-child, > .dropdown-toggle { .border-right-radius(0); } } .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { .border-left-radius(0); } // On active and open, don't show outline .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } // Sizing // // Remix the default button sizing classes into new ones for easier manipulation. .btn-group-xs > .btn { &:extend(.btn-xs); } .btn-group-sm > .btn { &:extend(.btn-sm); } .btn-group-lg > .btn { &:extend(.btn-lg); } // Split button dropdowns // ---------------------- // Give the line between buttons some depth .btn-group > .btn + .dropdown-toggle { padding-left: 8px; padding-right: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-left: 12px; padding-right: 12px; } // The clickable button for toggling the menu // Remove the gradient and set the same inset shadow as the :active state .btn-group.open .dropdown-toggle { .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); // Show no shadow for `.btn-link` since it has no other button styles. &.btn-link { .box-shadow(none); } } // Reposition the caret .btn .caret { margin-left: 0; } // Carets in other button sizes .btn-lg .caret { border-width: @caret-width-large @caret-width-large 0; border-bottom-width: 0; } // Upside down carets for .dropup .dropup .btn-lg .caret { border-width: 0 @caret-width-large @caret-width-large; } // Vertical button groups // ---------------------- .btn-group-vertical { > .btn, > .btn-group, > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } // Clear floats so dropdown menus can be properly placed > .btn-group { &:extend(.clearfix all); > .btn { float: none; } } > .btn + .btn, > .btn + .btn-group, > .btn-group + .btn, > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } } .btn-group-vertical > .btn { &:not(:first-child):not(:last-child) { border-radius: 0; } &:first-child:not(:last-child) { border-top-right-radius: @border-radius-base; .border-bottom-radius(0); } &:last-child:not(:first-child) { border-bottom-left-radius: @border-radius-base; .border-top-radius(0); } } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) { > .btn:last-child, > .dropdown-toggle { .border-bottom-radius(0); } } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { .border-top-radius(0); } // Justified button groups // ---------------------- .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; > .btn, > .btn-group { float: none; display: table-cell; width: 1%; } > .btn-group .btn { width: 100%; } > .btn-group .dropdown-menu { left: auto; } } // Checkbox and radio options // // In order to support the browser's form validation feedback, powered by the // `required` attribute, we have to "hide" the inputs via `clip`. We cannot use // `display: none;` or `visibility: hidden;` as that also hides the popover. // Simply visually hiding the inputs via `opacity` would leave them clickable in // certain cases which is prevented by using `clip` and `pointer-events`. // This way, we ensure a DOM element is visible to position the popover from. // // See https://github.com/twbs/bootstrap/pull/12794 and // https://github.com/twbs/bootstrap/pull/14559 for more information. [data-toggle="buttons"] { > .btn, > .btn-group > .btn { input[type="radio"], input[type="checkbox"] { position: absolute; clip: rect(0,0,0,0); pointer-events: none; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/buttons.less ================================================ // // Buttons // -------------------------------------------------- // Base styles // -------------------------------------------------- .btn { display: inline-block; margin-bottom: 0; // For input.btn font-weight: @btn-font-weight; text-align: center; vertical-align: middle; touch-action: manipulation; cursor: pointer; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid transparent; white-space: nowrap; .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); .user-select(none); &, &:active, &.active { &:focus, &.focus { .tab-focus(); } } &:hover, &:focus, &.focus { color: @btn-default-color; text-decoration: none; } &:active, &.active { outline: 0; background-image: none; .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); } &.disabled, &[disabled], fieldset[disabled] & { cursor: @cursor-disabled; .opacity(.65); .box-shadow(none); } a& { &.disabled, fieldset[disabled] & { pointer-events: none; // Future-proof disabling of clicks on `` elements } } } // Alternate buttons // -------------------------------------------------- .btn-default { .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); } .btn-primary { .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); } // Success appears as green .btn-success { .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); } // Info appears as blue-green .btn-info { .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); } // Warning appears as orange .btn-warning { .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); } // Danger and error appear as red .btn-danger { .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); } // Link buttons // ------------------------- // Make a button look and behave like a link .btn-link { color: @link-color; font-weight: normal; border-radius: 0; &, &:active, &.active, &[disabled], fieldset[disabled] & { background-color: transparent; .box-shadow(none); } &, &:hover, &:focus, &:active { border-color: transparent; } &:hover, &:focus { color: @link-hover-color; text-decoration: @link-hover-decoration; background-color: transparent; } &[disabled], fieldset[disabled] & { &:hover, &:focus { color: @btn-link-disabled-color; text-decoration: none; } } } // Button Sizes // -------------------------------------------------- .btn-lg { // line-height: ensure even-numbered height of button next to large input .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); } .btn-sm { // line-height: ensure proper height of button next to small input .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); } .btn-xs { .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); } // Block button // -------------------------------------------------- .btn-block { display: block; width: 100%; } // Vertically space out multiple block buttons .btn-block + .btn-block { margin-top: 5px; } // Specificity overrides input[type="submit"], input[type="reset"], input[type="button"] { &.btn-block { width: 100%; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/carousel.less ================================================ // // Carousel // -------------------------------------------------- // Wrapper for the slide container and indicators .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; > .item { display: none; position: relative; .transition(.6s ease-in-out left); // Account for jankitude on images > img, > a > img { &:extend(.img-responsive); line-height: 1; } // WebKit CSS3 transforms for supported devices @media all and (transform-3d), (-webkit-transform-3d) { .transition-transform(~'0.6s ease-in-out'); .backface-visibility(~'hidden'); .perspective(1000); &.next, &.active.right { .translate3d(100%, 0, 0); left: 0; } &.prev, &.active.left { .translate3d(-100%, 0, 0); left: 0; } &.next.left, &.prev.right, &.active { .translate3d(0, 0, 0); left: 0; } } } > .active, > .next, > .prev { display: block; } > .active { left: 0; } > .next, > .prev { position: absolute; top: 0; width: 100%; } > .next { left: 100%; } > .prev { left: -100%; } > .next.left, > .prev.right { left: 0; } > .active.left { left: -100%; } > .active.right { left: 100%; } } // Left/right controls for nav // --------------------------- .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: @carousel-control-width; .opacity(@carousel-control-opacity); font-size: @carousel-control-font-size; color: @carousel-control-color; text-align: center; text-shadow: @carousel-text-shadow; // We can't have this transition here because WebKit cancels the carousel // animation if you trip this while in the middle of another animation. // Set gradients for backgrounds &.left { #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); } &.right { left: auto; right: 0; #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); } // Hover/focus state &:hover, &:focus { outline: 0; color: @carousel-control-color; text-decoration: none; .opacity(.9); } // Toggles .icon-prev, .icon-next, .glyphicon-chevron-left, .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .icon-prev, .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .icon-next, .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .icon-prev, .icon-next { width: 20px; height: 20px; margin-top: -10px; line-height: 1; font-family: serif; } .icon-prev { &:before { content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) } } .icon-next { &:before { content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) } } } // Optional indicator pips // // Add an unordered list with the following class and add a list item for each // slide your carousel holds. .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid @carousel-indicator-border-color; border-radius: 10px; cursor: pointer; // IE8-9 hack for event handling // // Internet Explorer 8-9 does not support clicks on elements without a set // `background-color`. We cannot use `filter` since that's not viewed as a // background color by the browser. Thus, a hack is needed. // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer // // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we // set alpha transparency for the best results possible. background-color: #000 \9; // IE8 background-color: rgba(0,0,0,0); // IE9 } .active { margin: 0; width: 12px; height: 12px; background-color: @carousel-indicator-active-bg; } } // Optional captions // ----------------------------- // Hidden by default for smaller viewports .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: @carousel-caption-color; text-align: center; text-shadow: @carousel-text-shadow; & .btn { text-shadow: none; // No shadow for button elements in carousel-caption } } // Scale up controls for tablets and up @media screen and (min-width: @screen-sm-min) { // Scale up the controls a smidge .carousel-control { .glyphicon-chevron-left, .glyphicon-chevron-right, .icon-prev, .icon-next { width: 30px; height: 30px; margin-top: -15px; font-size: 30px; } .glyphicon-chevron-left, .icon-prev { margin-left: -15px; } .glyphicon-chevron-right, .icon-next { margin-right: -15px; } } // Show and left align the captions .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } // Move up the indicators .carousel-indicators { bottom: 20px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/close.less ================================================ // // Close icons // -------------------------------------------------- .close { float: right; font-size: (@font-size-base * 1.5); font-weight: @close-font-weight; line-height: 1; color: @close-color; text-shadow: @close-text-shadow; .opacity(.2); &:hover, &:focus { color: @close-color; text-decoration: none; cursor: pointer; .opacity(.5); } // Additional properties for button version // iOS requires the button element instead of an anchor tag. // If you want the anchor version, it requires `href="#"`. // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile button& { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/code.less ================================================ // // Code (inline and block) // -------------------------------------------------- // Inline and block code styles code, kbd, pre, samp { font-family: @font-family-monospace; } // Inline code code { padding: 2px 4px; font-size: 90%; color: @code-color; background-color: @code-bg; border-radius: @border-radius-base; } // User input typically entered via keyboard kbd { padding: 2px 4px; font-size: 90%; color: @kbd-color; background-color: @kbd-bg; border-radius: @border-radius-small; box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); kbd { padding: 0; font-size: 100%; font-weight: bold; box-shadow: none; } } // Blocks of code pre { display: block; padding: ((@line-height-computed - 1) / 2); margin: 0 0 (@line-height-computed / 2); font-size: (@font-size-base - 1); // 14px to 13px line-height: @line-height-base; word-break: break-all; word-wrap: break-word; color: @pre-color; background-color: @pre-bg; border: 1px solid @pre-border-color; border-radius: @border-radius-base; // Account for some code outputs that place code tags in pre tags code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } } // Enable scrollable blocks of code .pre-scrollable { max-height: @pre-scrollable-max-height; overflow-y: scroll; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/component-animations.less ================================================ // // Component animations // -------------------------------------------------- // Heads up! // // We don't use the `.opacity()` mixin here since it causes a bug with text // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. .fade { opacity: 0; .transition(opacity .15s linear); &.in { opacity: 1; } } .collapse { display: none; &.in { display: block; } tr&.in { display: table-row; } tbody&.in { display: table-row-group; } } .collapsing { position: relative; height: 0; overflow: hidden; .transition-property(~"height, visibility"); .transition-duration(.35s); .transition-timing-function(ease); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/dropdowns.less ================================================ // // Dropdown menus // -------------------------------------------------- // Dropdown arrow/caret .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: @caret-width-base dashed; border-right: @caret-width-base solid transparent; border-left: @caret-width-base solid transparent; } // The dropdown wrapper (div) .dropup, .dropdown { position: relative; } // Prevent the focus on the dropdown toggle when closing dropdowns .dropdown-toggle:focus { outline: 0; } // The dropdown menu (ul) .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: @zindex-dropdown; display: none; // none by default, but block on "open" of the menu float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; // override default ul list-style: none; font-size: @font-size-base; text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) background-color: @dropdown-bg; border: 1px solid @dropdown-fallback-border; // IE8 fallback border: 1px solid @dropdown-border; border-radius: @border-radius-base; .box-shadow(0 6px 12px rgba(0,0,0,.175)); background-clip: padding-box; // Aligns the dropdown menu to right // // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` &.pull-right { right: 0; left: auto; } // Dividers (basically an hr) within the dropdown .divider { .nav-divider(@dropdown-divider-bg); } // Links within the dropdown menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: @line-height-base; color: @dropdown-link-color; white-space: nowrap; // prevent links from randomly breaking onto new lines } } // Hover/Focus state .dropdown-menu > li > a { &:hover, &:focus { text-decoration: none; color: @dropdown-link-hover-color; background-color: @dropdown-link-hover-bg; } } // Active state .dropdown-menu > .active > a { &, &:hover, &:focus { color: @dropdown-link-active-color; text-decoration: none; outline: 0; background-color: @dropdown-link-active-bg; } } // Disabled state // // Gray out text and ensure the hover/focus state remains gray .dropdown-menu > .disabled > a { &, &:hover, &:focus { color: @dropdown-link-disabled-color; } // Nuke hover/focus effects &:hover, &:focus { text-decoration: none; background-color: transparent; background-image: none; // Remove CSS gradient .reset-filter(); cursor: @cursor-disabled; } } // Open state for the dropdown .open { // Show the menu > .dropdown-menu { display: block; } // Remove the outline when :focus is triggered > a { outline: 0; } } // Menu positioning // // Add extra class to `.dropdown-menu` to flip the alignment of the dropdown // menu with the parent. .dropdown-menu-right { left: auto; // Reset the default from `.dropdown-menu` right: 0; } // With v3, we enabled auto-flipping if you have a dropdown within a right // aligned nav component. To enable the undoing of that, we provide an override // to restore the default dropdown menu alignment. // // This is only for left-aligning a dropdown menu within a `.navbar-right` or // `.pull-right` nav component. .dropdown-menu-left { left: 0; right: auto; } // Dropdown section headers .dropdown-header { display: block; padding: 3px 20px; font-size: @font-size-small; line-height: @line-height-base; color: @dropdown-header-color; white-space: nowrap; // as with > li > a } // Backdrop to catch body clicks on mobile, etc. .dropdown-backdrop { position: fixed; left: 0; right: 0; bottom: 0; top: 0; z-index: (@zindex-dropdown - 10); } // Right aligned dropdowns .pull-right > .dropdown-menu { right: 0; left: auto; } // Allow for dropdowns to go bottom up (aka, dropup-menu) // // Just add .dropup after the standard .dropdown class and you're set, bro. // TODO: abstract this so that the navbar fixed styles are not placed here? .dropup, .navbar-fixed-bottom .dropdown { // Reverse the caret .caret { border-top: 0; border-bottom: @caret-width-base solid; content: ""; } // Different positioning for bottom up menu .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 2px; } } // Component alignment // // Reiterate per navbar.less and the modified component alignment there. @media (min-width: @grid-float-breakpoint) { .navbar-right { .dropdown-menu { .dropdown-menu-right(); } // Necessary for overrides of the default right aligned menu. // Will remove come v4 in all likelihood. .dropdown-menu-left { .dropdown-menu-left(); } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/forms.less ================================================ // // Forms // -------------------------------------------------- // Normalize non-controls // // Restyle and baseline non-control form elements. fieldset { padding: 0; margin: 0; border: 0; // Chrome and Firefox set a `min-width: min-content;` on fieldsets, // so we reset that to ensure it behaves more like a standard block element. // See https://github.com/twbs/bootstrap/issues/12359. min-width: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: @line-height-computed; font-size: (@font-size-base * 1.5); line-height: inherit; color: @legend-color; border: 0; border-bottom: 1px solid @legend-border-color; } label { display: inline-block; max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) margin-bottom: 5px; font-weight: bold; } // Normalize form controls // // While most of our form styles require extra classes, some basic normalization // is required to ensure optimum display with or without those classes to better // address browser inconsistencies. // Override content-box in Normalize (* isn't specific enough) input[type="search"] { .box-sizing(border-box); } // Position radios and checkboxes better input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; // IE8-9 line-height: normal; } // Set the height of file controls to match text inputs input[type="file"] { display: block; } // Make range inputs behave like textual form controls input[type="range"] { display: block; width: 100%; } // Make multiple select elements height not fixed select[multiple], select[size] { height: auto; } // Focus for file, radio, and checkbox input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { .tab-focus(); } // Adjust output element output { display: block; padding-top: (@padding-base-vertical + 1); font-size: @font-size-base; line-height: @line-height-base; color: @input-color; } // Common form controls // // Shared size and type resets for form controls. Apply `.form-control` to any // of the following form controls: // // select // textarea // input[type="text"] // input[type="password"] // input[type="datetime"] // input[type="datetime-local"] // input[type="date"] // input[type="month"] // input[type="time"] // input[type="week"] // input[type="number"] // input[type="email"] // input[type="url"] // input[type="search"] // input[type="tel"] // input[type="color"] .form-control { display: block; width: 100%; height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) padding: @padding-base-vertical @padding-base-horizontal; font-size: @font-size-base; line-height: @line-height-base; color: @input-color; background-color: @input-bg; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid @input-border; border-radius: @input-border-radius; // Note: This has no effect on s in CSS. .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); // Customize the `:focus` state to imitate native WebKit styles. .form-control-focus(); // Placeholder .placeholder(); // Disabled and read-only inputs // // HTML5 says that controls under a fieldset > legend:first-child won't be // disabled if the fieldset is disabled. Due to implementation difficulty, we // don't honor that edge case; we style them as disabled anyway. &[disabled], &[readonly], fieldset[disabled] & { background-color: @input-bg-disabled; opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 } &[disabled], fieldset[disabled] & { cursor: @cursor-disabled; } // Reset height for `textarea`s textarea& { height: auto; } } // Search inputs in iOS // // This overrides the extra rounded corners on search inputs in iOS so that our // `.form-control` class can properly style them. Note that this cannot simply // be added to `.form-control` as it's not specific enough. For details, see // https://github.com/twbs/bootstrap/issues/11586. input[type="search"] { -webkit-appearance: none; } // Special styles for iOS temporal inputs // // In Mobile Safari, setting `display: block` on temporal inputs causes the // text within the input to become vertically misaligned. As a workaround, we // set a pixel line-height that matches the given height of the input, but only // for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { line-height: @input-height-base; &.input-sm, .input-group-sm & { line-height: @input-height-small; } &.input-lg, .input-group-lg & { line-height: @input-height-large; } } } // Form groups // // Designed to help with the organization and spacing of vertical forms. For // horizontal forms, use the predefined grid classes. .form-group { margin-bottom: @form-group-margin-bottom; } // Checkboxes and radios // // Indent the labels to position radios/checkboxes as hanging controls. .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; label { min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-left: -20px; margin-top: 4px \9; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing } // Radios and checkboxes on same line .radio-inline, .checkbox-inline { position: relative; display: inline-block; padding-left: 20px; margin-bottom: 0; vertical-align: middle; font-weight: normal; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; // space out consecutive inline controls } // Apply same disabled cursor tweak as for inputs // Some special care is needed because Star // Import the fonts @font-face { font-family: 'Glyphicons Halflings'; src: url('@{icon-font-path}@{icon-font-name}.eot'); src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'), url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'), url('@{icon-font-path}@{icon-font-name}.woff') format('woff'), url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'), url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg'); } // Catchall baseclass .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } // Individual icons .glyphicon-asterisk { &:before { content: "\2a"; } } .glyphicon-plus { &:before { content: "\2b"; } } .glyphicon-euro, .glyphicon-eur { &:before { content: "\20ac"; } } .glyphicon-minus { &:before { content: "\2212"; } } .glyphicon-cloud { &:before { content: "\2601"; } } .glyphicon-envelope { &:before { content: "\2709"; } } .glyphicon-pencil { &:before { content: "\270f"; } } .glyphicon-glass { &:before { content: "\e001"; } } .glyphicon-music { &:before { content: "\e002"; } } .glyphicon-search { &:before { content: "\e003"; } } .glyphicon-heart { &:before { content: "\e005"; } } .glyphicon-star { &:before { content: "\e006"; } } .glyphicon-star-empty { &:before { content: "\e007"; } } .glyphicon-user { &:before { content: "\e008"; } } .glyphicon-film { &:before { content: "\e009"; } } .glyphicon-th-large { &:before { content: "\e010"; } } .glyphicon-th { &:before { content: "\e011"; } } .glyphicon-th-list { &:before { content: "\e012"; } } .glyphicon-ok { &:before { content: "\e013"; } } .glyphicon-remove { &:before { content: "\e014"; } } .glyphicon-zoom-in { &:before { content: "\e015"; } } .glyphicon-zoom-out { &:before { content: "\e016"; } } .glyphicon-off { &:before { content: "\e017"; } } .glyphicon-signal { &:before { content: "\e018"; } } .glyphicon-cog { &:before { content: "\e019"; } } .glyphicon-trash { &:before { content: "\e020"; } } .glyphicon-home { &:before { content: "\e021"; } } .glyphicon-file { &:before { content: "\e022"; } } .glyphicon-time { &:before { content: "\e023"; } } .glyphicon-road { &:before { content: "\e024"; } } .glyphicon-download-alt { &:before { content: "\e025"; } } .glyphicon-download { &:before { content: "\e026"; } } .glyphicon-upload { &:before { content: "\e027"; } } .glyphicon-inbox { &:before { content: "\e028"; } } .glyphicon-play-circle { &:before { content: "\e029"; } } .glyphicon-repeat { &:before { content: "\e030"; } } .glyphicon-refresh { &:before { content: "\e031"; } } .glyphicon-list-alt { &:before { content: "\e032"; } } .glyphicon-lock { &:before { content: "\e033"; } } .glyphicon-flag { &:before { content: "\e034"; } } .glyphicon-headphones { &:before { content: "\e035"; } } .glyphicon-volume-off { &:before { content: "\e036"; } } .glyphicon-volume-down { &:before { content: "\e037"; } } .glyphicon-volume-up { &:before { content: "\e038"; } } .glyphicon-qrcode { &:before { content: "\e039"; } } .glyphicon-barcode { &:before { content: "\e040"; } } .glyphicon-tag { &:before { content: "\e041"; } } .glyphicon-tags { &:before { content: "\e042"; } } .glyphicon-book { &:before { content: "\e043"; } } .glyphicon-bookmark { &:before { content: "\e044"; } } .glyphicon-print { &:before { content: "\e045"; } } .glyphicon-camera { &:before { content: "\e046"; } } .glyphicon-font { &:before { content: "\e047"; } } .glyphicon-bold { &:before { content: "\e048"; } } .glyphicon-italic { &:before { content: "\e049"; } } .glyphicon-text-height { &:before { content: "\e050"; } } .glyphicon-text-width { &:before { content: "\e051"; } } .glyphicon-align-left { &:before { content: "\e052"; } } .glyphicon-align-center { &:before { content: "\e053"; } } .glyphicon-align-right { &:before { content: "\e054"; } } .glyphicon-align-justify { &:before { content: "\e055"; } } .glyphicon-list { &:before { content: "\e056"; } } .glyphicon-indent-left { &:before { content: "\e057"; } } .glyphicon-indent-right { &:before { content: "\e058"; } } .glyphicon-facetime-video { &:before { content: "\e059"; } } .glyphicon-picture { &:before { content: "\e060"; } } .glyphicon-map-marker { &:before { content: "\e062"; } } .glyphicon-adjust { &:before { content: "\e063"; } } .glyphicon-tint { &:before { content: "\e064"; } } .glyphicon-edit { &:before { content: "\e065"; } } .glyphicon-share { &:before { content: "\e066"; } } .glyphicon-check { &:before { content: "\e067"; } } .glyphicon-move { &:before { content: "\e068"; } } .glyphicon-step-backward { &:before { content: "\e069"; } } .glyphicon-fast-backward { &:before { content: "\e070"; } } .glyphicon-backward { &:before { content: "\e071"; } } .glyphicon-play { &:before { content: "\e072"; } } .glyphicon-pause { &:before { content: "\e073"; } } .glyphicon-stop { &:before { content: "\e074"; } } .glyphicon-forward { &:before { content: "\e075"; } } .glyphicon-fast-forward { &:before { content: "\e076"; } } .glyphicon-step-forward { &:before { content: "\e077"; } } .glyphicon-eject { &:before { content: "\e078"; } } .glyphicon-chevron-left { &:before { content: "\e079"; } } .glyphicon-chevron-right { &:before { content: "\e080"; } } .glyphicon-plus-sign { &:before { content: "\e081"; } } .glyphicon-minus-sign { &:before { content: "\e082"; } } .glyphicon-remove-sign { &:before { content: "\e083"; } } .glyphicon-ok-sign { &:before { content: "\e084"; } } .glyphicon-question-sign { &:before { content: "\e085"; } } .glyphicon-info-sign { &:before { content: "\e086"; } } .glyphicon-screenshot { &:before { content: "\e087"; } } .glyphicon-remove-circle { &:before { content: "\e088"; } } .glyphicon-ok-circle { &:before { content: "\e089"; } } .glyphicon-ban-circle { &:before { content: "\e090"; } } .glyphicon-arrow-left { &:before { content: "\e091"; } } .glyphicon-arrow-right { &:before { content: "\e092"; } } .glyphicon-arrow-up { &:before { content: "\e093"; } } .glyphicon-arrow-down { &:before { content: "\e094"; } } .glyphicon-share-alt { &:before { content: "\e095"; } } .glyphicon-resize-full { &:before { content: "\e096"; } } .glyphicon-resize-small { &:before { content: "\e097"; } } .glyphicon-exclamation-sign { &:before { content: "\e101"; } } .glyphicon-gift { &:before { content: "\e102"; } } .glyphicon-leaf { &:before { content: "\e103"; } } .glyphicon-fire { &:before { content: "\e104"; } } .glyphicon-eye-open { &:before { content: "\e105"; } } .glyphicon-eye-close { &:before { content: "\e106"; } } .glyphicon-warning-sign { &:before { content: "\e107"; } } .glyphicon-plane { &:before { content: "\e108"; } } .glyphicon-calendar { &:before { content: "\e109"; } } .glyphicon-random { &:before { content: "\e110"; } } .glyphicon-comment { &:before { content: "\e111"; } } .glyphicon-magnet { &:before { content: "\e112"; } } .glyphicon-chevron-up { &:before { content: "\e113"; } } .glyphicon-chevron-down { &:before { content: "\e114"; } } .glyphicon-retweet { &:before { content: "\e115"; } } .glyphicon-shopping-cart { &:before { content: "\e116"; } } .glyphicon-folder-close { &:before { content: "\e117"; } } .glyphicon-folder-open { &:before { content: "\e118"; } } .glyphicon-resize-vertical { &:before { content: "\e119"; } } .glyphicon-resize-horizontal { &:before { content: "\e120"; } } .glyphicon-hdd { &:before { content: "\e121"; } } .glyphicon-bullhorn { &:before { content: "\e122"; } } .glyphicon-bell { &:before { content: "\e123"; } } .glyphicon-certificate { &:before { content: "\e124"; } } .glyphicon-thumbs-up { &:before { content: "\e125"; } } .glyphicon-thumbs-down { &:before { content: "\e126"; } } .glyphicon-hand-right { &:before { content: "\e127"; } } .glyphicon-hand-left { &:before { content: "\e128"; } } .glyphicon-hand-up { &:before { content: "\e129"; } } .glyphicon-hand-down { &:before { content: "\e130"; } } .glyphicon-circle-arrow-right { &:before { content: "\e131"; } } .glyphicon-circle-arrow-left { &:before { content: "\e132"; } } .glyphicon-circle-arrow-up { &:before { content: "\e133"; } } .glyphicon-circle-arrow-down { &:before { content: "\e134"; } } .glyphicon-globe { &:before { content: "\e135"; } } .glyphicon-wrench { &:before { content: "\e136"; } } .glyphicon-tasks { &:before { content: "\e137"; } } .glyphicon-filter { &:before { content: "\e138"; } } .glyphicon-briefcase { &:before { content: "\e139"; } } .glyphicon-fullscreen { &:before { content: "\e140"; } } .glyphicon-dashboard { &:before { content: "\e141"; } } .glyphicon-paperclip { &:before { content: "\e142"; } } .glyphicon-heart-empty { &:before { content: "\e143"; } } .glyphicon-link { &:before { content: "\e144"; } } .glyphicon-phone { &:before { content: "\e145"; } } .glyphicon-pushpin { &:before { content: "\e146"; } } .glyphicon-usd { &:before { content: "\e148"; } } .glyphicon-gbp { &:before { content: "\e149"; } } .glyphicon-sort { &:before { content: "\e150"; } } .glyphicon-sort-by-alphabet { &:before { content: "\e151"; } } .glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } } .glyphicon-sort-by-order { &:before { content: "\e153"; } } .glyphicon-sort-by-order-alt { &:before { content: "\e154"; } } .glyphicon-sort-by-attributes { &:before { content: "\e155"; } } .glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } } .glyphicon-unchecked { &:before { content: "\e157"; } } .glyphicon-expand { &:before { content: "\e158"; } } .glyphicon-collapse-down { &:before { content: "\e159"; } } .glyphicon-collapse-up { &:before { content: "\e160"; } } .glyphicon-log-in { &:before { content: "\e161"; } } .glyphicon-flash { &:before { content: "\e162"; } } .glyphicon-log-out { &:before { content: "\e163"; } } .glyphicon-new-window { &:before { content: "\e164"; } } .glyphicon-record { &:before { content: "\e165"; } } .glyphicon-save { &:before { content: "\e166"; } } .glyphicon-open { &:before { content: "\e167"; } } .glyphicon-saved { &:before { content: "\e168"; } } .glyphicon-import { &:before { content: "\e169"; } } .glyphicon-export { &:before { content: "\e170"; } } .glyphicon-send { &:before { content: "\e171"; } } .glyphicon-floppy-disk { &:before { content: "\e172"; } } .glyphicon-floppy-saved { &:before { content: "\e173"; } } .glyphicon-floppy-remove { &:before { content: "\e174"; } } .glyphicon-floppy-save { &:before { content: "\e175"; } } .glyphicon-floppy-open { &:before { content: "\e176"; } } .glyphicon-credit-card { &:before { content: "\e177"; } } .glyphicon-transfer { &:before { content: "\e178"; } } .glyphicon-cutlery { &:before { content: "\e179"; } } .glyphicon-header { &:before { content: "\e180"; } } .glyphicon-compressed { &:before { content: "\e181"; } } .glyphicon-earphone { &:before { content: "\e182"; } } .glyphicon-phone-alt { &:before { content: "\e183"; } } .glyphicon-tower { &:before { content: "\e184"; } } .glyphicon-stats { &:before { content: "\e185"; } } .glyphicon-sd-video { &:before { content: "\e186"; } } .glyphicon-hd-video { &:before { content: "\e187"; } } .glyphicon-subtitles { &:before { content: "\e188"; } } .glyphicon-sound-stereo { &:before { content: "\e189"; } } .glyphicon-sound-dolby { &:before { content: "\e190"; } } .glyphicon-sound-5-1 { &:before { content: "\e191"; } } .glyphicon-sound-6-1 { &:before { content: "\e192"; } } .glyphicon-sound-7-1 { &:before { content: "\e193"; } } .glyphicon-copyright-mark { &:before { content: "\e194"; } } .glyphicon-registration-mark { &:before { content: "\e195"; } } .glyphicon-cloud-download { &:before { content: "\e197"; } } .glyphicon-cloud-upload { &:before { content: "\e198"; } } .glyphicon-tree-conifer { &:before { content: "\e199"; } } .glyphicon-tree-deciduous { &:before { content: "\e200"; } } .glyphicon-cd { &:before { content: "\e201"; } } .glyphicon-save-file { &:before { content: "\e202"; } } .glyphicon-open-file { &:before { content: "\e203"; } } .glyphicon-level-up { &:before { content: "\e204"; } } .glyphicon-copy { &:before { content: "\e205"; } } .glyphicon-paste { &:before { content: "\e206"; } } // The following 2 Glyphicons are omitted for the time being because // they currently use Unicode codepoints that are outside the // Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle // non-BMP codepoints in CSS string escapes, and thus can't display these two icons. // Notably, the bug affects some older versions of the Android Browser. // More info: https://github.com/twbs/bootstrap/issues/10106 // .glyphicon-door { &:before { content: "\1f6aa"; } } // .glyphicon-key { &:before { content: "\1f511"; } } .glyphicon-alert { &:before { content: "\e209"; } } .glyphicon-equalizer { &:before { content: "\e210"; } } .glyphicon-king { &:before { content: "\e211"; } } .glyphicon-queen { &:before { content: "\e212"; } } .glyphicon-pawn { &:before { content: "\e213"; } } .glyphicon-bishop { &:before { content: "\e214"; } } .glyphicon-knight { &:before { content: "\e215"; } } .glyphicon-baby-formula { &:before { content: "\e216"; } } .glyphicon-tent { &:before { content: "\26fa"; } } .glyphicon-blackboard { &:before { content: "\e218"; } } .glyphicon-bed { &:before { content: "\e219"; } } .glyphicon-apple { &:before { content: "\f8ff"; } } .glyphicon-erase { &:before { content: "\e221"; } } .glyphicon-hourglass { &:before { content: "\231b"; } } .glyphicon-lamp { &:before { content: "\e223"; } } .glyphicon-duplicate { &:before { content: "\e224"; } } .glyphicon-piggy-bank { &:before { content: "\e225"; } } .glyphicon-scissors { &:before { content: "\e226"; } } .glyphicon-bitcoin { &:before { content: "\e227"; } } .glyphicon-btc { &:before { content: "\e227"; } } .glyphicon-xbt { &:before { content: "\e227"; } } .glyphicon-yen { &:before { content: "\00a5"; } } .glyphicon-jpy { &:before { content: "\00a5"; } } .glyphicon-ruble { &:before { content: "\20bd"; } } .glyphicon-rub { &:before { content: "\20bd"; } } .glyphicon-scale { &:before { content: "\e230"; } } .glyphicon-ice-lolly { &:before { content: "\e231"; } } .glyphicon-ice-lolly-tasted { &:before { content: "\e232"; } } .glyphicon-education { &:before { content: "\e233"; } } .glyphicon-option-horizontal { &:before { content: "\e234"; } } .glyphicon-option-vertical { &:before { content: "\e235"; } } .glyphicon-menu-hamburger { &:before { content: "\e236"; } } .glyphicon-modal-window { &:before { content: "\e237"; } } .glyphicon-oil { &:before { content: "\e238"; } } .glyphicon-grain { &:before { content: "\e239"; } } .glyphicon-sunglasses { &:before { content: "\e240"; } } .glyphicon-text-size { &:before { content: "\e241"; } } .glyphicon-text-color { &:before { content: "\e242"; } } .glyphicon-text-background { &:before { content: "\e243"; } } .glyphicon-object-align-top { &:before { content: "\e244"; } } .glyphicon-object-align-bottom { &:before { content: "\e245"; } } .glyphicon-object-align-horizontal{ &:before { content: "\e246"; } } .glyphicon-object-align-left { &:before { content: "\e247"; } } .glyphicon-object-align-vertical { &:before { content: "\e248"; } } .glyphicon-object-align-right { &:before { content: "\e249"; } } .glyphicon-triangle-right { &:before { content: "\e250"; } } .glyphicon-triangle-left { &:before { content: "\e251"; } } .glyphicon-triangle-bottom { &:before { content: "\e252"; } } .glyphicon-triangle-top { &:before { content: "\e253"; } } .glyphicon-console { &:before { content: "\e254"; } } .glyphicon-superscript { &:before { content: "\e255"; } } .glyphicon-subscript { &:before { content: "\e256"; } } .glyphicon-menu-left { &:before { content: "\e257"; } } .glyphicon-menu-right { &:before { content: "\e258"; } } .glyphicon-menu-down { &:before { content: "\e259"; } } .glyphicon-menu-up { &:before { content: "\e260"; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/grid.less ================================================ // // Grid system // -------------------------------------------------- // Container widths // // Set the container width, and override it for fixed navbars in media queries. .container { .container-fixed(); @media (min-width: @screen-sm-min) { width: @container-sm; } @media (min-width: @screen-md-min) { width: @container-md; } @media (min-width: @screen-lg-min) { width: @container-lg; } } // Fluid container // // Utilizes the mixin meant for fixed width containers, but without any defined // width for fluid, full width layouts. .container-fluid { .container-fixed(); } // Row // // Rows contain and clear the floats of your columns. .row { .make-row(); } // Columns // // Common styles for small and large grid columns .make-grid-columns(); // Extra small grid // // Columns, offsets, pushes, and pulls for extra small devices like // smartphones. .make-grid(xs); // Small grid // // Columns, offsets, pushes, and pulls for the small device range, from phones // to tablets. @media (min-width: @screen-sm-min) { .make-grid(sm); } // Medium grid // // Columns, offsets, pushes, and pulls for the desktop device range. @media (min-width: @screen-md-min) { .make-grid(md); } // Large grid // // Columns, offsets, pushes, and pulls for the large desktop device range. @media (min-width: @screen-lg-min) { .make-grid(lg); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/input-groups.less ================================================ // // Input groups // -------------------------------------------------- // Base styles // ------------------------- .input-group { position: relative; // For dropdowns display: table; border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table // Undo padding and float of grid classes &[class*="col-"] { float: none; padding-left: 0; padding-right: 0; } .form-control { // Ensure that the input is always above the *appended* addon button for // proper border colors. position: relative; z-index: 2; // IE9 fubars the placeholder attribute in text inputs and the arrows on // select elements in input groups. To fix it, we float the input. Details: // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 float: left; width: 100%; margin-bottom: 0; } } // Sizing options // // Remix the default form control sizing classes into new ones for easier // manipulation. .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { .input-lg(); } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { .input-sm(); } // Display as table-cell // ------------------------- .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; &:not(:first-child):not(:last-child) { border-radius: 0; } } // Addon and addon wrapper for buttons .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; // Match the inputs } // Text input groups // ------------------------- .input-group-addon { padding: @padding-base-vertical @padding-base-horizontal; font-size: @font-size-base; font-weight: normal; line-height: 1; color: @input-color; text-align: center; background-color: @input-group-addon-bg; border: 1px solid @input-group-addon-border-color; border-radius: @border-radius-base; // Sizing &.input-sm { padding: @padding-small-vertical @padding-small-horizontal; font-size: @font-size-small; border-radius: @border-radius-small; } &.input-lg { padding: @padding-large-vertical @padding-large-horizontal; font-size: @font-size-large; border-radius: @border-radius-large; } // Nuke default margins from checkboxes and radios to vertically center within. input[type="radio"], input[type="checkbox"] { margin-top: 0; } } // Reset rounded corners .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { .border-right-radius(0); } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { .border-left-radius(0); } .input-group-addon:last-child { border-left: 0; } // Button input groups // ------------------------- .input-group-btn { position: relative; // Jankily prevent input button groups from wrapping with `white-space` and // `font-size` in combination with `inline-block` on buttons. font-size: 0; white-space: nowrap; // Negative margin for spacing, position for bringing hovered/focused/actived // element above the siblings. > .btn { position: relative; + .btn { margin-left: -1px; } // Bring the "active" button to the front &:hover, &:focus, &:active { z-index: 2; } } // Negative margin to only have a 1px border between the two &:first-child { > .btn, > .btn-group { margin-right: -1px; } } &:last-child { > .btn, > .btn-group { margin-left: -1px; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/jumbotron.less ================================================ // // Jumbotron // -------------------------------------------------- .jumbotron { padding: @jumbotron-padding (@jumbotron-padding / 2); margin-bottom: @jumbotron-padding; color: @jumbotron-color; background-color: @jumbotron-bg; h1, .h1 { color: @jumbotron-heading-color; } p { margin-bottom: (@jumbotron-padding / 2); font-size: @jumbotron-font-size; font-weight: 200; } > hr { border-top-color: darken(@jumbotron-bg, 10%); } .container &, .container-fluid & { border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container } .container { max-width: 100%; } @media screen and (min-width: @screen-sm-min) { padding: (@jumbotron-padding * 1.6) 0; .container &, .container-fluid & { padding-left: (@jumbotron-padding * 2); padding-right: (@jumbotron-padding * 2); } h1, .h1 { font-size: @jumbotron-heading-font-size; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/labels.less ================================================ // // Labels // -------------------------------------------------- .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: @label-color; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; // Add hover effects, but only for links a& { &:hover, &:focus { color: @label-link-hover-color; text-decoration: none; cursor: pointer; } } // Empty labels collapse automatically (not available in IE8) &:empty { display: none; } // Quick fix for labels in buttons .btn & { position: relative; top: -1px; } } // Colors // Contextual variations (linked labels get darker on :hover) .label-default { .label-variant(@label-default-bg); } .label-primary { .label-variant(@label-primary-bg); } .label-success { .label-variant(@label-success-bg); } .label-info { .label-variant(@label-info-bg); } .label-warning { .label-variant(@label-warning-bg); } .label-danger { .label-variant(@label-danger-bg); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/list-group.less ================================================ // // List groups // -------------------------------------------------- // Base class // // Easily usable on
                              ,
                                , or
                                . .list-group { // No need to set list-style: none; since .list-group-item is block level margin-bottom: 20px; padding-left: 0; // reset padding because ul and ol } // Individual list items // // Use on `li`s or `div`s within the `.list-group` parent. .list-group-item { position: relative; display: block; padding: 10px 15px; // Place the border on the list items and negative margin up for better styling margin-bottom: -1px; background-color: @list-group-bg; border: 1px solid @list-group-border; // Round the first and last items &:first-child { .border-top-radius(@list-group-border-radius); } &:last-child { margin-bottom: 0; .border-bottom-radius(@list-group-border-radius); } } // Linked list items // // Use anchor elements instead of `li`s or `div`s to create linked list items. // Includes an extra `.active` modifier class for showing selected items. a.list-group-item { color: @list-group-link-color; .list-group-item-heading { color: @list-group-link-heading-color; } // Hover state &:hover, &:focus { text-decoration: none; color: @list-group-link-hover-color; background-color: @list-group-hover-bg; } } .list-group-item { // Disabled state &.disabled, &.disabled:hover, &.disabled:focus { background-color: @list-group-disabled-bg; color: @list-group-disabled-color; cursor: @cursor-disabled; // Force color to inherit for custom content .list-group-item-heading { color: inherit; } .list-group-item-text { color: @list-group-disabled-text-color; } } // Active class on item itself, not parent &.active, &.active:hover, &.active:focus { z-index: 2; // Place active items above their siblings for proper border styling color: @list-group-active-color; background-color: @list-group-active-bg; border-color: @list-group-active-border; // Force color to inherit for custom content .list-group-item-heading, .list-group-item-heading > small, .list-group-item-heading > .small { color: inherit; } .list-group-item-text { color: @list-group-active-text-color; } } } // Contextual variants // // Add modifier classes to change text and background color on individual items. // Organizationally, this must come after the `:hover` states. .list-group-item-variant(success; @state-success-bg; @state-success-text); .list-group-item-variant(info; @state-info-bg; @state-info-text); .list-group-item-variant(warning; @state-warning-bg; @state-warning-text); .list-group-item-variant(danger; @state-danger-bg; @state-danger-text); // Custom content options // // Extra classes for creating well-formatted content within `.list-group-item`s. .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/media.less ================================================ .media { // Proper spacing between instances of .media margin-top: 15px; &:first-child { margin-top: 0; } } .media, .media-body { zoom: 1; overflow: hidden; } .media-body { width: 10000px; } .media-object { display: block; } .media-right, .media > .pull-right { padding-left: 10px; } .media-left, .media > .pull-left { padding-right: 10px; } .media-left, .media-right, .media-body { display: table-cell; vertical-align: top; } .media-middle { vertical-align: middle; } .media-bottom { vertical-align: bottom; } // Reset margins on headings for tighter default spacing .media-heading { margin-top: 0; margin-bottom: 5px; } // Media list variation // // Undo default ul/ol styles .media-list { padding-left: 0; list-style: none; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/alerts.less ================================================ // Alerts .alert-variant(@background; @border; @text-color) { background-color: @background; border-color: @border; color: @text-color; hr { border-top-color: darken(@border, 5%); } .alert-link { color: darken(@text-color, 10%); } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/background-variant.less ================================================ // Contextual backgrounds .bg-variant(@color) { background-color: @color; a&:hover, a&:focus { background-color: darken(@color, 10%); } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/border-radius.less ================================================ // Single side border-radius .border-top-radius(@radius) { border-top-right-radius: @radius; border-top-left-radius: @radius; } .border-right-radius(@radius) { border-bottom-right-radius: @radius; border-top-right-radius: @radius; } .border-bottom-radius(@radius) { border-bottom-right-radius: @radius; border-bottom-left-radius: @radius; } .border-left-radius(@radius) { border-bottom-left-radius: @radius; border-top-left-radius: @radius; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/buttons.less ================================================ // Button variants // // Easily pump out default styles, as well as :hover, :focus, :active, // and disabled options for all buttons .button-variant(@color; @background; @border) { color: @color; background-color: @background; border-color: @border; &:focus, &.focus { color: @color; background-color: darken(@background, 10%); border-color: darken(@border, 25%); } &:hover { color: @color; background-color: darken(@background, 10%); border-color: darken(@border, 12%); } &:active, &.active, .open > .dropdown-toggle& { color: @color; background-color: darken(@background, 10%); border-color: darken(@border, 12%); &:hover, &:focus, &.focus { color: @color; background-color: darken(@background, 17%); border-color: darken(@border, 25%); } } &:active, &.active, .open > .dropdown-toggle& { background-image: none; } &.disabled, &[disabled], fieldset[disabled] & { &, &:hover, &:focus, &.focus, &:active, &.active { background-color: @background; border-color: @border; } } .badge { color: @background; background-color: @color; } } // Button sizes .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { padding: @padding-vertical @padding-horizontal; font-size: @font-size; line-height: @line-height; border-radius: @border-radius; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/center-block.less ================================================ // Center-align a block level element .center-block() { display: block; margin-left: auto; margin-right: auto; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/clearfix.less ================================================ // Clearfix // // For modern browsers // 1. The space content is one way to avoid an Opera bug when the // contenteditable attribute is included anywhere else in the document. // Otherwise it causes space to appear at the top and bottom of elements // that are clearfixed. // 2. The use of `table` rather than `block` is only necessary if using // `:before` to contain the top-margins of child elements. // // Source: http://nicolasgallagher.com/micro-clearfix-hack/ .clearfix() { &:before, &:after { content: " "; // 1 display: table; // 2 } &:after { clear: both; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/mixins/forms.less ================================================ // Form validation states // // Used in forms.less to generate the form validation CSS for warnings, errors, // and successes. .form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { // Color the label and help text .help-block, .control-label, .radio, .checkbox, .radio-inline, .checkbox-inline, &.radio label, &.checkbox label, &.radio-inline label, &.checkbox-inline label { color: @text-color; } // Set the border and box shadow on specific inputs to match .form-control { border-color: @border-color; .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work &:focus { border-color: darken(@border-color, 10%); @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); .box-shadow(@shadow); } } // Set validation states also for addons .input-group-addon { color: @text-color; border-color: @border-color; background-color: @background-color; } // Optional feedback icon .form-control-feedback { color: @text-color; } } // Form control focus state // // Generate a customized focus state and for any input with the specified color, // which defaults to the `@input-border-focus` variable. // // We highly encourage you to not customize the default value, but instead use // this to tweak colors on an as-needed basis. This aesthetic change is based on // WebKit's default styles, but applicable to a wider range of browsers. Its // usability and accessibility should be taken into account with any change. // // Example usage: change the default blue border and shadow to white for better // contrast against a dark gray background. .form-control-focus(@color: @input-border-focus) { @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); &:focus { border-color: @color; outline: 0; .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); } } // Form control sizing // // Relative text size, padding, and border-radii changes for form controls. For // horizontal sizing, wrap controls in the predefined grid classes. `
                            ", cell: "", footer: "

                            ", header: "

                            ", headerCell: "
                            ", icon: "", infos: "
                            {{lbl.infos}}
                            ", loading: "
                            ", noResults: "", pagination: "
                              ", paginationItem: "
                            • {{ctx.text}}
                            • ", rawHeaderCell: "
                              ", // Used for the multi select box row: "{{ctx.cells}}", search: "
                              ", select: "" } }; /** * Appends rows. * * @method append * @param rows {Array} An array of rows to append * @chainable **/ Grid.prototype.append = function(rows) { if (this.options.ajax) { // todo: implement ajax PUT } else { var appendedRows = []; for (var i = 0; i < rows.length; i++) { if (appendRow.call(this, rows[i])) { appendedRows.push(rows[i]); } } sortRows.call(this); highlightAppendedRows.call(this, appendedRows); loadData.call(this); this.element.trigger("appended" + namespace, [appendedRows]); } return this; }; /** * Removes all rows. * * @method clear * @chainable **/ Grid.prototype.clear = function() { if (this.options.ajax) { // todo: implement ajax POST } else { var removedRows = $.extend([], this.rows); this.rows = []; this.current = 1; this.total = 0; loadData.call(this); this.element.trigger("cleared" + namespace, [removedRows]); } return this; }; /** * Removes the control functionality completely and transforms the current state to the initial HTML structure. * * @method destroy * @chainable **/ Grid.prototype.destroy = function() { // todo: this method has to be optimized (the complete initial state must be restored) $(window).off(namespace); if (this.options.navigation & 1) { this.header.remove(); } if (this.options.navigation & 2) { this.footer.remove(); } this.element.before(this.origin).remove(); return this; }; /** * Resets the state and reloads rows. * * @method reload * @chainable **/ Grid.prototype.reload = function() { this.current = 1; // reset loadData.call(this); return this; }; /** * Removes rows by ids. Removes selected rows if no ids are provided. * * @method remove * @param [rowsIds] {Array} An array of rows ids to remove * @chainable **/ Grid.prototype.remove = function(rowIds) { if (this.identifier != null) { var that = this; if (this.options.ajax) { // todo: implement ajax DELETE } else { rowIds = rowIds || this.selectedRows; var id, removedRows = []; for (var i = 0; i < rowIds.length; i++) { id = rowIds[i]; for (var j = 0; j < this.rows.length; j++) { if (this.rows[j][this.identifier] === id) { removedRows.push(this.rows[j]); this.rows.splice(j, 1); break; } } } this.current = 1; // reset loadData.call(this); this.element.trigger("removed" + namespace, [removedRows]); } } return this; }; /** * Searches in all rows for a specific phrase (but only in visible cells). * The search filter will be reseted, if no argument is provided. * * @method search * @param [phrase] {String} The phrase to search for * @chainable **/ Grid.prototype.search = function(phrase) { phrase = phrase || ""; if (this.searchPhrase !== phrase) { var selector = getCssSelector(this.options.css.searchField), searchFields = findFooterAndHeaderItems.call(this, selector); searchFields.val(phrase); } executeSearch.call(this, phrase); return this; }; /** * Selects rows by ids. Selects all visible rows if no ids are provided. * In server-side scenarios only visible rows are selectable. * * @method select * @param [rowsIds] {Array} An array of rows ids to select * @chainable **/ Grid.prototype.select = function(rowIds) { if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); var id, i, selectedRows = []; while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) { id = rowIds.pop(); if ($.inArray(id, this.selectedRows) === -1) { for (i = 0; i < this.currentRows.length; i++) { if (this.currentRows[i][this.identifier] === id) { selectedRows.push(this.currentRows[i]); this.selectedRows.push(id); break; } } } } if (selectedRows.length > 0) { var selectBoxSelector = getCssSelector(this.options.css.selectBox), selectMultiSelectBox = this.selectedRows.length >= this.currentRows.length; i = 0; while (!this.options.keepSelection && selectMultiSelectBox && i < this.currentRows.length) { selectMultiSelectBox = ($.inArray(this.currentRows[i++][this.identifier], this.selectedRows) !== -1); } this.element.find("thead " + selectBoxSelector).prop("checked", selectMultiSelectBox); if (!this.options.multiSelect) { this.element.find("tbody > tr " + selectBoxSelector + ":checked") .trigger("click" + namespace); } for (i = 0; i < this.selectedRows.length; i++) { this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") .addClass(this.options.css.selected)._bgAria("selected", "true") .find(selectBoxSelector).prop("checked", true); } this.element.trigger("selected" + namespace, [selectedRows]); } } return this; }; /** * Deselects rows by ids. Deselects all visible rows if no ids are provided. * In server-side scenarios only visible rows are deselectable. * * @method deselect * @param [rowsIds] {Array} An array of rows ids to deselect * @chainable **/ Grid.prototype.deselect = function(rowIds) { if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); var id, i, pos, deselectedRows = []; while (rowIds.length > 0) { id = rowIds.pop(); pos = $.inArray(id, this.selectedRows); if (pos !== -1) { for (i = 0; i < this.currentRows.length; i++) { if (this.currentRows[i][this.identifier] === id) { deselectedRows.push(this.currentRows[i]); this.selectedRows.splice(pos, 1); break; } } } } if (deselectedRows.length > 0) { var selectBoxSelector = getCssSelector(this.options.css.selectBox); this.element.find("thead " + selectBoxSelector).prop("checked", false); for (i = 0; i < deselectedRows.length; i++) { this.element.find("tbody > tr[data-row-id=\"" + deselectedRows[i][this.identifier] + "\"]") .removeClass(this.options.css.selected)._bgAria("selected", "false") .find(selectBoxSelector).prop("checked", false); } this.element.trigger("deselected" + namespace, [deselectedRows]); } } return this; }; /** * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. * * @method sort * @param [dictionary] {Object} A sort descriptor dictionary that contains the sort information * @chainable **/ Grid.prototype.sort = function(dictionary) { var values = (dictionary) ? $.extend({}, dictionary) : {}; if (values === this.sortDictionary) { return this; } this.sortDictionary = values; renderTableHeader.call(this); sortRows.call(this); loadData.call(this); return this; }; /** * Gets a list of the column settings. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getColumnSettings * @return {Array} Returns a list of the column settings. * @since 1.2.0 **/ Grid.prototype.getColumnSettings = function() { return $.merge([], this.columns); }; /** * Gets the current page index. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getCurrentPage * @return {Number} Returns the current page index. * @since 1.2.0 **/ Grid.prototype.getCurrentPage = function() { return this.current; }; /** * Gets the current rows. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getCurrentPage * @return {Array} Returns the current rows. * @since 1.2.0 **/ Grid.prototype.getCurrentRows = function() { return $.merge([], this.currentRows); }; /** * Gets a number represents the row count per page. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getRowCount * @return {Number} Returns the row count per page. * @since 1.2.0 **/ Grid.prototype.getRowCount = function() { return this.rowCount; }; /** * Gets the actual search phrase. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSearchPhrase * @return {String} Returns the actual search phrase. * @since 1.2.0 **/ Grid.prototype.getSearchPhrase = function() { return this.searchPhrase; }; /** * Gets the complete list of currently selected rows. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSelectedRows * @return {Array} Returns all selected rows. * @since 1.2.0 **/ Grid.prototype.getSelectedRows = function() { return $.merge([], this.selectedRows); }; /** * Gets the sort dictionary which represents the state of column sorting. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSortDictionary * @return {Object} Returns the sort dictionary. * @since 1.2.0 **/ Grid.prototype.getSortDictionary = function() { return $.extend({}, this.sortDictionary); }; /** * Gets a number represents the total page count. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getTotalPageCount * @return {Number} Returns the total page count. * @since 1.2.0 **/ Grid.prototype.getTotalPageCount = function() { return this.totalPages; }; /** * Gets a number represents the total row count. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getTotalRowCount * @return {Number} Returns the total row count. * @since 1.2.0 **/ Grid.prototype.getTotalRowCount = function() { return this.total; }; // GRID COMMON TYPE EXTENSIONS // ============ $.fn.extend({ _bgAria: function (name, value) { return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); }, _bgBusyAria: function(busy) { return (busy == null || busy) ? this._bgAria("busy", "true") : this._bgAria("busy", "false"); }, _bgRemoveAria: function (name) { return this.removeAttr("aria-" + name); }, _bgEnableAria: function (enable) { return (enable == null || enable) ? this.removeClass("disabled")._bgAria("disabled", "false") : this.addClass("disabled")._bgAria("disabled", "true"); }, _bgEnableField: function (enable) { return (enable == null || enable) ? this.removeAttr("disabled") : this.attr("disabled", "disable"); }, _bgShowAria: function (show) { return (show == null || show) ? this.show()._bgAria("hidden", "false") : this.hide()._bgAria("hidden", "true"); }, _bgSelectAria: function (select) { return (select == null || select) ? this.addClass("active")._bgAria("selected", "true") : this.removeClass("active")._bgAria("selected", "false"); }, _bgId: function (id) { return (id) ? this.attr("id", id) : this.attr("id"); } }); if (!String.prototype.resolve) { var formatter = { "checked": function(value) { if (typeof value === "boolean") { return (value) ? "checked=\"checked\"" : ""; } return value; } }; String.prototype.resolve = function (substitutes, prefixes) { var result = this; $.each(substitutes, function (key, value) { if (value != null && typeof value !== "function") { if (typeof value === "object") { var keys = (prefixes) ? $.extend([], prefixes) : []; keys.push(key); result = result.resolve(value, keys) + ""; } else { if (formatter && formatter[key] && typeof formatter[key] === "function") { value = formatter[key](value); } key = (prefixes) ? prefixes.join(".") + "." + key : key; var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); } } }); return result; }; } if (!Array.prototype.first) { Array.prototype.first = function (condition) { for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { return item; } } return null; }; } if (!Array.prototype.contains) { Array.prototype.contains = function (condition) { for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { return true; } } return false; }; } if (!Array.prototype.page) { Array.prototype.page = function (page, size) { var skip = (page - 1) * size, end = skip + size; return (this.length > skip) ? (this.length > end) ? this.slice(skip, end) : this.slice(skip) : []; }; } if (!Array.prototype.where) { Array.prototype.where = function (condition) { var result = []; for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { result.push(item); } } return result; }; } if (!Array.prototype.propValues) { Array.prototype.propValues = function (propName) { var result = []; for (var i = 0; i < this.length; i++) { result.push(this[i][propName]); } return result; }; } // GRID PLUGIN DEFINITION // ===================== var old = $.fn.bootgrid; $.fn.bootgrid = function (option) { var args = Array.prototype.slice.call(arguments, 1), returnValue = null, elements = this.each(function (index) { var $this = $(this), instance = $this.data(namespace), options = typeof option === "object" && option; if (!instance && option === "destroy") { return; } if (!instance) { $this.data(namespace, (instance = new Grid(this, options))); init.call(instance); } if (typeof option === "string") { if (option.indexOf("get") === 0 && index === 0) { returnValue = instance[option].apply(instance, args); } else if (option.indexOf("get") !== 0) { return instance[option].apply(instance, args); } } }); return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; }; $.fn.bootgrid.Constructor = Grid; // GRID NO CONFLICT // =============== $.fn.bootgrid.noConflict = function () { $.fn.bootgrid = old; return this; }; // GRID DATA-API // ============ $("[data-toggle=\"bootgrid\"]").bootgrid(); })(jQuery, window); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/jquery.bootgrid.updated.js ================================================ /*! * jQuery Bootgrid v1.3.1 - 09/11/2015 * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) { /*jshint validthis: true */ "use strict"; // GRID INTERNAL FIELDS // ==================== var namespace = ".rs.jquery.bootgrid"; // GRID INTERNAL FUNCTIONS // ===================== function appendRow(row) { var that = this; function exists(item) { return that.identifier && item[that.identifier] === row[that.identifier]; } if (!this.rows.contains(exists)) { this.rows.push(row); return true; } return false; } function findFooterAndHeaderItems(selector) { var footer = (this.footer) ? this.footer.find(selector) : $(), header = (this.header) ? this.header.find(selector) : $(); return $.merge(footer, header); } function getParams(context) { return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : this.cachedParams; } function getRequest() { var request = { current: this.current, rowCount: this.rowCount, sort: this.sortDictionary, searchPhrase: this.searchPhrase }, post = this.options.post; post = ($.isFunction(post)) ? post() : post; return this.options.requestHandler($.extend(true, request, post)); } function getCssSelector(css) { return "." + $.trim(css).replace(/\s+/gm, "."); } function getUrl() { var url = this.options.url; return ($.isFunction(url)) ? url() : url; } function init() { this.element.trigger("initialize" + namespace); loadColumns.call(this); // Loads columns from HTML thead tag this.selection = this.options.selection && this.identifier != null; loadRows.call(this); // Loads rows from HTML tbody tag if ajax is false prepareTable.call(this); renderTableHeader.call(this); renderSearchField.call(this); renderActions.call(this); loadData.call(this); this.element.trigger("initialized" + namespace); } function highlightAppendedRows(rows) { if (this.options.highlightRows) { // todo: implement } } function isVisible(column) { return column.visible; } function loadColumns() { var that = this, firstHeadRow = this.element.find("thead > tr").first(), sorted = false; /*jshint -W018*/ firstHeadRow.children().each(function () { var $this = $(this), data = $this.data(), column = { id: data.columnId, identifier: that.identifier == null && data.identifier || false, converter: that.options.converters[data.converter || data.type] || that.options.converters["string"], text: $this.text(), align: data.align || "left", headerAlign: data.headerAlign || "left", cssClass: data.cssClass || "", headerCssClass: data.headerCssClass || "", formatter: that.options.formatters[data.formatter] || null, order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, searchable: !(data.searchable === false), // default: true sortable: !(data.sortable === false), // default: true visible: !(data.visible === false), // default: true visibleInSelection: !(data.visibleInSelection === false), // default: true width: ($.isNumeric(data.width)) ? data.width + "px" : (typeof(data.width) === "string") ? data.width : null }; that.columns.push(column); if (column.order != null) { that.sortDictionary[column.id] = column.order; } // Prevents multiple identifiers if (column.identifier) { that.identifier = column.id; that.converter = column.converter; } // ensures that only the first order will be applied in case of multi sorting is disabled if (!that.options.multiSort && column.order !== null) { sorted = true; } }); /*jshint +W018*/ } /* response = { current: 1, rowCount: 10, rows: [{}, {}], sort: [{ "columnId": "asc" }], total: 101 } */ function loadData() { var that = this; this.element._bgBusyAria(true).trigger("load" + namespace); showLoading.call(this); function containsPhrase(row) { var column, searchPattern = new RegExp(that.searchPhrase, (that.options.caseSensitive) ? "g" : "gi"); for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && column.visible && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; } } return false; } function update(rows, total) { that.currentRows = rows; setTotals.call(that, total); if (!that.options.keepSelection) { that.selectedRows = []; } renderRows.call(that, rows); renderInfos.call(that); renderPagination.call(that); that.element._bgBusyAria(false).trigger("loaded" + namespace); } if (this.options.ajax) { var request = getRequest.call(this), url = getUrl.call(this); if (url == null || typeof url !== "string" || url.length === 0) { throw new Error("Url setting must be a none empty string or a function that returns one."); } // aborts the previous ajax request if not already finished or failed if (this.xqr) { this.xqr.abort(); } var settings = { url: url, data: request, success: function(response) { that.xqr = null; if (typeof (response) === "string") { response = $.parseJSON(response); } response = that.options.responseHandler(response); that.current = response.current; update(response.rows, response.total); }, error: function (jqXHR, textStatus, errorThrown) { that.xqr = null; if (textStatus !== "abort") { renderNoResultsRow.call(that); // overrides loading mask that.element._bgBusyAria(false).trigger("loaded" + namespace); } } }; settings = $.extend(this.options.ajaxSettings, settings); this.xqr = $.ajax(settings); } else { var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, total = rows.length; if (this.rowCount !== -1) { rows = rows.page(this.current, this.rowCount); } // todo: improve the following comment // setTimeout decouples the initialization so that adding event handlers happens before window.setTimeout(function () { update(rows, total); }, 10); } } function loadRows() { if (!this.options.ajax) { var that = this, rows = this.element.find("tbody > tr"); rows.each(function () { var $this = $(this), cells = $this.children("td"), row = {}; $.each(that.columns, function (i, column) { row[column.id] = column.converter.from(cells.eq(i).text()); }); appendRow.call(that, row); }); setTotals.call(this, this.rows.length); sortRows.call(this); } } function setTotals(total) { this.total = total; this.totalPages = (this.rowCount === -1) ? 1 : Math.ceil(this.total / this.rowCount); } function prepareTable() { var tpl = this.options.templates, wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? this.element.parent() : this.element; this.element.addClass(this.options.css.table); // checks whether there is an tbody element; otherwise creates one if (this.element.children("tbody").length === 0) { this.element.append(tpl.body); } if (this.options.navigation & 1) { this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); wrapper.before(this.header); } if (this.options.navigation & 2) { this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); wrapper.after(this.footer); } } function renderActions() { if (this.options.navigation !== 0) { var css = this.options.css, selector = getCssSelector(css.actions), actionItems = findFooterAndHeaderItems.call(this, selector); if (actionItems.length > 0) { var that = this, tpl = this.options.templates, actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button if (this.options.ajax) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), refresh = $(tpl.actionButton.resolve(getParams.call(this, { content: refreshIcon, text: this.options.labels.refresh }))) .on("click" + namespace, function (e) { // todo: prevent multiple fast clicks (fast click detection) e.stopPropagation(); that.current = 1; loadData.call(that); }); actions.append(refresh); } // Row count selection renderRowCountSelection.call(this, actions); // Column selection renderColumnSelection.call(this, actions); replacePlaceHolder.call(this, actionItems, actions); } } } function renderColumnSelection(actions) { if (this.options.columnSelection && this.columns.length > 1) { var that = this, css = this.options.css, tpl = this.options.templates, icon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconColumns })), dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: icon }))), selector = getCssSelector(css.dropDownItem), checkboxSelector = getCssSelector(css.dropDownItemCheckbox), itemsSelector = getCssSelector(css.dropDownMenuItems); $.each(this.columns, function (i, column) { if (column.visibleInSelection) { var item = $(tpl.actionDropDownCheckboxItem.resolve(getParams.call(that, { name: column.id, label: column.text, checked: column.visible }))) .on("click" + namespace, selector, function (e) { e.stopPropagation(); var $this = $(this), checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { column.visible = checkbox.prop("checked"); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); that.element.find("tbody").empty(); // Fixes an column visualization bug renderTableHeader.call(that); loadData.call(that); } }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); } }); actions.append(dropDown); } } function renderInfos() { if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.infos), infoItems = findFooterAndHeaderItems.call(this, selector); if (infoItems.length > 0) { var end = (this.current * this.rowCount), infos = $(this.options.templates.infos.resolve(getParams.call(this, { end: (this.total === 0 || end === -1 || end > this.total) ? this.total : end, start: (this.total === 0) ? 0 : (end - this.rowCount + 1), total: this.total }))); replacePlaceHolder.call(this, infoItems, infos); } } } function renderNoResultsRow() { var tbody = this.element.children("tbody").first(), tpl = this.options.templates, count = this.columns.where(isVisible).length; if (this.selection) { count = count + 1; } tbody.html(tpl.noResults.resolve(getParams.call(this, { columns: count }))); } function renderPagination() { if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.pagination), paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); if (this.rowCount !== -1 && paginationItems.length > 0) { var tpl = this.options.templates, current = this.current, totalPages = this.totalPages, pagination = $(tpl.pagination.resolve(getParams.call(this))), offsetRight = totalPages - current, offsetLeft = (this.options.padding - current) * -1, startWith = ((offsetRight >= this.options.padding) ? Math.max(offsetLeft, 1) : Math.max((offsetLeft - this.options.padding + offsetRight), 1)), maxCount = this.options.padding * 2 + 1, count = (totalPages >= maxCount) ? maxCount : totalPages; renderPaginationItem.call(this, pagination, "first", "", "first") ._bgEnableAria(current > 1); renderPaginationItem.call(this, pagination, "prev", "", "prev") ._bgEnableAria(current > 1); for (var i = 0; i < count; i++) { var pos = i + startWith; renderPaginationItem.call(this, pagination, pos, pos, "page-" + pos) ._bgEnableAria()._bgSelectAria(pos === current); } if (count === 0) { renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) ._bgEnableAria(false)._bgSelectAria(); } renderPaginationItem.call(this, pagination, "next", "", "next") ._bgEnableAria(totalPages > current); renderPaginationItem.call(this, pagination, "last", "", "last") ._bgEnableAria(totalPages > current); replacePlaceHolder.call(this, paginationItems, pagination); } } } function renderPaginationItem(list, page, text, markerCss) { var that = this, tpl = this.options.templates, css = this.options.css, values = getParams.call(this, { css: markerCss, text: text, page: page }), item = $(tpl.paginationItem.resolve(values)) .on("click" + namespace, getCssSelector(css.paginationButton), function (e) { e.stopPropagation(); e.preventDefault(); var $this = $(this), parent = $this.parent(); if (!parent.hasClass("active") && !parent.hasClass("disabled")) { var commandList = { first: 1, prev: that.current - 1, next: that.current + 1, last: that.totalPages }; var command = $this.data("page"); that.current = commandList[command] || command; loadData.call(that); } $this.trigger("blur"); }); list.append(item); return item; } function renderRowCountSelection(actions) { var that = this, rowCountList = this.options.rowCount; function getText(value) { return (value === -1) ? that.options.labels.all : value; } if ($.isArray(rowCountList)) { var css = this.options.css, tpl = this.options.templates, dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), menuSelector = getCssSelector(css.dropDownMenu), menuTextSelector = getCssSelector(css.dropDownMenuText), menuItemsSelector = getCssSelector(css.dropDownMenuItems), menuItemSelector = getCssSelector(css.dropDownItemButton); $.each(rowCountList, function (index, value) { var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, { text: getText(value), action: value }))) ._bgSelectAria(value === that.rowCount) .on("click" + namespace, menuItemSelector, function (e) { e.preventDefault(); var $this = $(this), newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { // todo: sophisticated solution needed for calculating which page is selected that.current = 1; // that.rowCount === -1 ---> All that.rowCount = newRowCount; $this.parents(menuItemsSelector).children().each(function () { var $item = $(this), currentRowCount = $item.find(menuItemSelector).data("action"); $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); loadData.call(that); } }); dropDown.find(menuItemsSelector).append(item); }); actions.append(dropDown); } } function renderRows(rows) { if (rows.length > 0) { var that = this, css = this.options.css, tpl = this.options.templates, tbody = this.element.children("tbody").first(), allRowsSelected = true, html = ""; $.each(rows, function (index, row) { var cells = "", rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", rowCss = ""; if (that.selection) { var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), selectBox = tpl.select.resolve(getParams.call(that, { type: "checkbox", value: row[that.identifier], checked: selected })); cells += tpl.cell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); allRowsSelected = (allRowsSelected && selected); if (selected) { rowCss += css.selected; rowAttr += " aria-selected=\"true\""; } } var status = row.status != null && that.options.statusMapping[row.status]; if (status) { rowCss += status; } $.each(that.columns, function (j, column) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : column.converter.to(row[column.id]), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, css: ((column.align === "right") ? css.right : (column.align === "center") ? css.center : css.left) + cssClass, style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); if (rowCss.length > 0) { rowAttr += " class=\"" + rowCss + "\""; } html += tpl.row.resolve(getParams.call(that, { attr: rowAttr, cells: cells })); }); // sets or clears multi selectbox state that.element.find("thead " + getCssSelector(that.options.css.selectBox)) .prop("checked", allRowsSelected); tbody.html(html); registerRowEvents.call(this, tbody); } else { renderNoResultsRow.call(this); } } function registerRowEvents(tbody) { var that = this, selectBoxSelector = getCssSelector(this.options.css.selectBox); if (this.selection) { tbody.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) { e.stopPropagation(); var $this = $(this), id = that.converter.from($this.val()); if ($this.prop("checked")) { that.select([id]); } else { that.deselect([id]); } }); } tbody.off("click" + namespace, "> tr") .on("click" + namespace, "> tr", function(e) { e.stopPropagation(); var $this = $(this), id = (that.identifier == null) ? $this.data("row-id") : that.converter.from($this.data("row-id") + ""), row = (that.identifier == null) ? that.currentRows[id] : that.currentRows.first(function (item) { return item[that.identifier] === id; }); if (that.selection && that.options.rowSelect) { if ($this.hasClass(that.options.css.selected)) { that.deselect([id]); } else { that.select([id]); } } that.element.trigger("click" + namespace, [that.columns, row]); }); } function renderSearchField() { if (this.options.navigation !== 0) { var css = this.options.css, selector = getCssSelector(css.search), searchItems = findFooterAndHeaderItems.call(this, selector); if (searchItems.length > 0) { var that = this, tpl = this.options.templates, timer = null, // fast keyup detection currentValue = "", searchFieldSelector = getCssSelector(css.searchField), search = $(tpl.search.resolve(getParams.call(this))), searchField = (search.is(searchFieldSelector)) ? search : search.find(searchFieldSelector); searchField.on("keyup" + namespace, function (e) { e.stopPropagation(); var newValue = $(this).val(); if (currentValue !== newValue || (e.which === 13 && newValue !== "")) { currentValue = newValue; if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { window.clearTimeout(timer); timer = window.setTimeout(function () { executeSearch.call(that, newValue); }, that.options.searchSettings.delay); } } }); replacePlaceHolder.call(this, searchItems, search); } } } function executeSearch(phrase) { if (this.searchPhrase !== phrase) { this.current = 1; this.searchPhrase = phrase; loadData.call(this); } } function renderTableHeader() { var that = this, headerRow = this.element.find("thead > tr"), css = this.options.css, tpl = this.options.templates, html = "", sorting = this.options.sorting; if (this.selection) { var selectBox = (this.options.multiSelect) ? tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); } $.each(this.columns, function (index, column) { if (column.visible) { var sortOrder = that.sortDictionary[column.id], iconCss = ((sorting && sortOrder && sortOrder === "asc") ? css.iconUp : (sorting && sortOrder && sortOrder === "desc") ? css.iconDown : ""), icon = tpl.icon.resolve(getParams.call(that, { iconCss: iconCss })), align = column.headerAlign, cssClass = (column.headerCssClass.length > 0) ? " " + column.headerCssClass : ""; html += tpl.headerCell.resolve(getParams.call(that, { column: column, icon: icon, sortable: sorting && column.sortable && css.sortable || "", css: ((align === "right") ? css.right : (align === "center") ? css.center : css.left) + cssClass, style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); headerRow.html(html); if (sorting) { var sortingSelector = getCssSelector(css.sortable); headerRow.off("click" + namespace, sortingSelector) .on("click" + namespace, sortingSelector, function (e) { e.preventDefault(); setTableHeaderSortDirection.call(that, $(this)); sortRows.call(that); loadData.call(that); }); } // todo: create a own function for that piece of code if (this.selection && this.options.multiSelect) { var selectBoxSelector = getCssSelector(css.selectBox); headerRow.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) { e.stopPropagation(); if ($(this).prop("checked")) { that.select(); } else { that.deselect(); } }); } } function setTableHeaderSortDirection(element) { var css = this.options.css, iconSelector = getCssSelector(css.icon), columnId = element.data("column-id") || element.parents("th").first().data("column-id"), sortOrder = this.sortDictionary[columnId], icon = element.find(iconSelector); if (!this.options.multiSort) { element.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); this.sortDictionary = {}; } if (sortOrder && sortOrder === "asc") { this.sortDictionary[columnId] = "desc"; icon.removeClass(css.iconUp).addClass(css.iconDown); } else if (sortOrder && sortOrder === "desc") { if (this.options.multiSort) { var newSort = {}; for (var key in this.sortDictionary) { if (key !== columnId) { newSort[key] = this.sortDictionary[key]; } } this.sortDictionary = newSort; icon.removeClass(css.iconDown); } else { this.sortDictionary[columnId] = "asc"; icon.removeClass(css.iconDown).addClass(css.iconUp); } } else { this.sortDictionary[columnId] = "asc"; icon.addClass(css.iconUp); } } function replacePlaceHolder(placeholder, element) { placeholder.each(function (index, item) { // todo: check how append is implemented. Perhaps cloning here is superfluous. $(item).before(element.clone(true)).remove(); }); } function showLoading() { var that = this; window.setTimeout(function() { if (that.element._bgAria("busy") === "true") { var tpl = that.options.templates, thead = that.element.children("thead").first(), tbody = that.element.children("tbody").first(), firstCell = tbody.find("tr > td").first(), padding = (that.element.height() - thead.height()) - (firstCell.height() + 20), count = that.columns.where(isVisible).length; if (that.selection) { count = count + 1; } tbody.html(tpl.loading.resolve(getParams.call(that, { columns: count }))); if (that.rowCount !== -1 && padding > 0) { tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); } } }, 250); } function sortRows() { var sortArray = []; function sort(x, y, current) { current = current || 0; var next = current + 1, item = sortArray[current]; function sortOrder(value) { return (item.order === "asc") ? value : value * -1; } return (x[item.id] > y[item.id]) ? sortOrder(1) : (x[item.id] < y[item.id]) ? sortOrder(-1) : (sortArray.length > next) ? sort(x, y, next) : 0; } if (!this.options.ajax) { var that = this; for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ id: key, order: this.sortDictionary[key] }); } } if (sortArray.length > 0) { this.rows.sort(sort); } } } // GRID PUBLIC CLASS DEFINITION // ==================== /** * Represents the jQuery Bootgrid plugin. * * @class Grid * @constructor * @param element {Object} The corresponding DOM element. * @param options {Object} The options to override default settings. * @chainable **/ var Grid = function(element, options) { this.element = $(element); this.origin = this.element.clone(); this.options = $.extend(true, {}, Grid.defaults, this.element.data(), options); // overrides rowCount explicitly because deep copy ($.extend) leads to strange behaviour var rowCount = this.options.rowCount = this.element.data().rowCount || options.rowCount || this.options.rowCount; this.columns = []; this.current = 1; this.currentRows = []; this.identifier = null; // The first column ID that is marked as identifier this.selection = false; this.converter = null; // The converter for the column that is marked as identifier this.rowCount = ($.isArray(rowCount)) ? rowCount[0] : rowCount; this.rows = []; this.searchPhrase = ""; this.selectedRows = []; this.sortDictionary = {}; this.total = 0; this.totalPages = 0; this.cachedParams = { lbl: this.options.labels, css: this.options.css, ctx: {} }; this.header = null; this.footer = null; this.xqr = null; // todo: implement cache }; /** * An object that represents the default settings. * * @static * @class defaults * @for Grid * @example * // Global approach * $.bootgrid.defaults.selection = true; * @example * // Initialization approach * $("#bootgrid").bootgrid({ selection = true }); **/ Grid.defaults = { navigation: 3, // it's a flag: 0 = none, 1 = top, 2 = bottom, 3 = both (top and bottom) padding: 2, // page padding (pagination) columnSelection: true, rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") /** * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. * * @property selection * @type Boolean * @default false * @for defaults * @since 1.0.0 **/ selection: false, /** * Enables multi selection (`selection` must be set to `true` as well). Default value is `false`. * * @property multiSelect * @type Boolean * @default false * @for defaults * @since 1.0.0 **/ multiSelect: false, /** * Enables entire row click selection (`selection` must be set to `true` as well). Default value is `false`. * * @property rowSelect * @type Boolean * @default false * @for defaults * @since 1.1.0 **/ rowSelect: false, /** * Defines whether the row selection is saved internally on filtering, paging and sorting * (even if the selected rows are not visible). * * @property keepSelection * @type Boolean * @default false * @for defaults * @since 1.1.0 **/ keepSelection: false, highlightRows: false, // highlights new rows (find the page of the first new row) sorting: true, multiSort: false, /** * General search settings to configure the search field behaviour. * * @property searchSettings * @type Object * @for defaults * @since 1.2.0 **/ searchSettings: { /** * The time in milliseconds to wait before search gets executed. * * @property delay * @type Number * @default 250 * @for searchSettings **/ delay: 250, /** * The characters to type before the search gets executed. * * @property characters * @type Number * @default 1 * @for searchSettings **/ characters: 1 }, /** * Defines whether the data shall be loaded via an asynchronous HTTP (Ajax) request. * * @property ajax * @type Boolean * @default false * @for defaults **/ ajax: false, /** * Ajax request settings that shall be used for server-side communication. * All setting except data, error, success and url can be overridden. * For the full list of settings go to http://api.jquery.com/jQuery.ajax/. * * @property ajaxSettings * @type Object * @for defaults * @since 1.2.0 **/ ajaxSettings: { /** * Specifies the HTTP method which shall be used when sending data to the server. * Go to http://api.jquery.com/jQuery.ajax/ for more details. * This setting is overriden for backward compatibility. * * @property method * @type String * @default "POST" * @for ajaxSettings **/ method: "POST" }, /** * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` * that returns a `PlainObject` can be passed. Default value is `{}`. * * @property post * @type Object|Function * @default function (request) { return request; } * @for defaults * @deprecated Use instead `requestHandler` **/ post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") /** * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` * that returns a `String` can be passed. Default value is `""`. * * @property url * @type String|Function * @default "" * @for defaults **/ url: "", // or use function () { return ""; } /** * Defines whether the search is case sensitive or insensitive. * * @property caseSensitive * @type Boolean * @default true * @for defaults * @since 1.1.0 **/ caseSensitive: true, // note: The following properties should not be used via data-api attributes /** * Transforms the JSON request object in what ever is needed on the server-side implementation. * * @property requestHandler * @type Function * @default function (request) { return request; } * @for defaults * @since 1.1.0 **/ requestHandler: function (request) { return request; }, /** * Transforms the response object into the expected JSON response object. * * @property responseHandler * @type Function * @default function (response) { return response; } * @for defaults * @since 1.1.0 **/ responseHandler: function (response) { return response; }, /** * A list of converters. * * @property converters * @type Object * @for defaults * @since 1.0.0 **/ converters: { numeric: { from: function (value) { return +value; }, // converts from string to numeric to: function (value) { return value + ""; } // converts from numeric to string }, string: { // default converter from: function (value) { return value; }, to: function (value) { return value; } } }, /** * Contains all css classes. * * @property css * @type Object * @for defaults **/ css: { actions: "actions btn-group", // must be a unique class name or constellation of class names within the header and footer center: "text-center", columnHeaderAnchor: "column-header-anchor", // must be a unique class name or constellation of class names within the column header cell columnHeaderText: "text", dropDownItem: "dropdown-item", // must be a unique class name or constellation of class names within the actionDropDown, dropDownItemButton: "dropdown-item-button", // must be a unique class name or constellation of class names within the actionDropDown dropDownItemCheckbox: "dropdown-item-checkbox", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenu: "dropdown btn-group", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenuItems: "dropdown-menu pull-right", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenuText: "dropdown-text", // must be a unique class name or constellation of class names within the actionDropDown footer: "bootgrid-footer container-fluid", header: "bootgrid-header container-fluid", icon: "icon glyphicon", iconColumns: "glyphicon-th-list", iconDown: "glyphicon-chevron-down", iconRefresh: "glyphicon-refresh", iconSearch: "glyphicon-search", iconUp: "glyphicon-chevron-up", infos: "infos", // must be a unique class name or constellation of class names within the header and footer, left: "text-left", pagination: "pagination", // must be a unique class name or constellation of class names within the header and footer paginationButton: "button", // must be a unique class name or constellation of class names within the pagination /** * CSS class to select the parent div which activates responsive mode. * * @property responsiveTable * @type String * @default "table-responsive" * @for css * @since 1.1.0 **/ responsiveTable: "table-responsive", right: "text-right", search: "search form-group", // must be a unique class name or constellation of class names within the header and footer searchField: "search-field form-control", selectBox: "select-box", // must be a unique class name or constellation of class names within the entire table selectCell: "select-cell", // must be a unique class name or constellation of class names within the entire table /** * CSS class to highlight selected rows. * * @property selected * @type String * @default "active" * @for css * @since 1.1.0 **/ selected: "active", sortable: "sortable", table: "bootgrid-table table" }, /** * A dictionary of formatters. * * @property formatters * @type Object * @for defaults * @since 1.0.0 **/ formatters: {}, /** * Contains all labels. * * @property labels * @type Object * @for defaults **/ labels: { all: "All", infos: "Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries", loading: "Loading...", noResults: "No results found!", refresh: "Refresh", search: "Search" }, /** * Specifies the mapping between status and contextual classes to color rows. * * @property statusMapping * @type Object * @for defaults * @since 1.2.0 **/ statusMapping: { /** * Specifies a successful or positive action. * * @property 0 * @type String * @for statusMapping **/ 0: "success", /** * Specifies a neutral informative change or action. * * @property 1 * @type String * @for statusMapping **/ 1: "info", /** * Specifies a warning that might need attention. * * @property 2 * @type String * @for statusMapping **/ 2: "warning", /** * Specifies a dangerous or potentially negative action. * * @property 3 * @type String * @for statusMapping **/ 3: "danger" }, /** * Contains all templates. * * @property templates * @type Object * @for defaults **/ templates: { actionButton: "", actionDropDown: "
                                ", actionDropDownItem: "
                              • {{ctx.text}}
                              • ", actionDropDownCheckboxItem: "
                              • ", actions: "
                                ", body: "
                                ", cell: "", footer: "

                                ", header: "

                                ", headerCell: "
                                ", icon: "", infos: "
                                {{lbl.infos}}
                                ", loading: "
                                ", noResults: "", pagination: "
                                  ", paginationItem: "
                                • {{ctx.text}}
                                • ", rawHeaderCell: "
                                  ", // Used for the multi select box row: "{{ctx.cells}}", search: "
                                  ", select: "
                                  " } }; /** * Appends rows. * * @method append * @param rows {Array} An array of rows to append * @chainable **/ Grid.prototype.append = function(rows) { if (this.options.ajax) { // todo: implement ajax PUT } else { var appendedRows = []; for (var i = 0; i < rows.length; i++) { if (appendRow.call(this, rows[i])) { appendedRows.push(rows[i]); } } sortRows.call(this); highlightAppendedRows.call(this, appendedRows); loadData.call(this); this.element.trigger("appended" + namespace, [appendedRows]); } return this; }; /** * Removes all rows. * * @method clear * @chainable **/ Grid.prototype.clear = function() { if (this.options.ajax) { // todo: implement ajax POST } else { var removedRows = $.extend([], this.rows); this.rows = []; this.current = 1; this.total = 0; loadData.call(this); this.element.trigger("cleared" + namespace, [removedRows]); } return this; }; /** * Removes the control functionality completely and transforms the current state to the initial HTML structure. * * @method destroy * @chainable **/ Grid.prototype.destroy = function() { // todo: this method has to be optimized (the complete initial state must be restored) $(window).off(namespace); if (this.options.navigation & 1) { this.header.remove(); } if (this.options.navigation & 2) { this.footer.remove(); } this.element.before(this.origin).remove(); return this; }; /** * Resets the state and reloads rows. * * @method reload * @chainable **/ Grid.prototype.reload = function() { this.current = 1; // reset loadData.call(this); return this; }; /** * Removes rows by ids. Removes selected rows if no ids are provided. * * @method remove * @param [rowsIds] {Array} An array of rows ids to remove * @chainable **/ Grid.prototype.remove = function(rowIds) { if (this.identifier != null) { var that = this; if (this.options.ajax) { // todo: implement ajax DELETE } else { rowIds = rowIds || this.selectedRows; var id, removedRows = []; for (var i = 0; i < rowIds.length; i++) { id = rowIds[i]; for (var j = 0; j < this.rows.length; j++) { if (this.rows[j][this.identifier] === id) { removedRows.push(this.rows[j]); this.rows.splice(j, 1); break; } } } this.current = 1; // reset loadData.call(this); this.element.trigger("removed" + namespace, [removedRows]); } } return this; }; /** * Searches in all rows for a specific phrase (but only in visible cells). * The search filter will be reseted, if no argument is provided. * * @method search * @param [phrase] {String} The phrase to search for * @chainable **/ Grid.prototype.search = function(phrase) { phrase = phrase || ""; if (this.searchPhrase !== phrase) { var selector = getCssSelector(this.options.css.searchField), searchFields = findFooterAndHeaderItems.call(this, selector); searchFields.val(phrase); } executeSearch.call(this, phrase); return this; }; /** * Selects rows by ids. Selects all visible rows if no ids are provided. * In server-side scenarios only visible rows are selectable. * * @method select * @param [rowsIds] {Array} An array of rows ids to select * @chainable **/ Grid.prototype.select = function(rowIds) { if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); var id, i, selectedRows = []; while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) { id = rowIds.pop(); if ($.inArray(id, this.selectedRows) === -1) { for (i = 0; i < this.currentRows.length; i++) { if (this.currentRows[i][this.identifier] === id) { selectedRows.push(this.currentRows[i]); this.selectedRows.push(id); break; } } } } if (selectedRows.length > 0) { var selectBoxSelector = getCssSelector(this.options.css.selectBox), selectMultiSelectBox = this.selectedRows.length >= this.currentRows.length; i = 0; while (!this.options.keepSelection && selectMultiSelectBox && i < this.currentRows.length) { selectMultiSelectBox = ($.inArray(this.currentRows[i++][this.identifier], this.selectedRows) !== -1); } this.element.find("thead " + selectBoxSelector).prop("checked", selectMultiSelectBox); if (!this.options.multiSelect) { this.element.find("tbody > tr " + selectBoxSelector + ":checked") .trigger("click" + namespace); } for (i = 0; i < this.selectedRows.length; i++) { this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") .addClass(this.options.css.selected)._bgAria("selected", "true") .find(selectBoxSelector).prop("checked", true); } this.element.trigger("selected" + namespace, [selectedRows]); } } return this; }; /** * Deselects rows by ids. Deselects all visible rows if no ids are provided. * In server-side scenarios only visible rows are deselectable. * * @method deselect * @param [rowsIds] {Array} An array of rows ids to deselect * @chainable **/ Grid.prototype.deselect = function(rowIds) { if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); var id, i, pos, deselectedRows = []; while (rowIds.length > 0) { id = rowIds.pop(); pos = $.inArray(id, this.selectedRows); if (pos !== -1) { for (i = 0; i < this.currentRows.length; i++) { if (this.currentRows[i][this.identifier] === id) { deselectedRows.push(this.currentRows[i]); this.selectedRows.splice(pos, 1); break; } } } } if (deselectedRows.length > 0) { var selectBoxSelector = getCssSelector(this.options.css.selectBox); this.element.find("thead " + selectBoxSelector).prop("checked", false); for (i = 0; i < deselectedRows.length; i++) { this.element.find("tbody > tr[data-row-id=\"" + deselectedRows[i][this.identifier] + "\"]") .removeClass(this.options.css.selected)._bgAria("selected", "false") .find(selectBoxSelector).prop("checked", false); } this.element.trigger("deselected" + namespace, [deselectedRows]); } } return this; }; /** * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. * * @method sort * @param [dictionary] {Object} A sort descriptor dictionary that contains the sort information * @chainable **/ Grid.prototype.sort = function(dictionary) { var values = (dictionary) ? $.extend({}, dictionary) : {}; if (values === this.sortDictionary) { return this; } this.sortDictionary = values; renderTableHeader.call(this); sortRows.call(this); loadData.call(this); return this; }; /** * Gets a list of the column settings. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getColumnSettings * @return {Array} Returns a list of the column settings. * @since 1.2.0 **/ Grid.prototype.getColumnSettings = function() { return $.merge([], this.columns); }; /** * Gets the current page index. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getCurrentPage * @return {Number} Returns the current page index. * @since 1.2.0 **/ Grid.prototype.getCurrentPage = function() { return this.current; }; /** * Gets the current rows. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getCurrentPage * @return {Array} Returns the current rows. * @since 1.2.0 **/ Grid.prototype.getCurrentRows = function() { return $.merge([], this.currentRows); }; /** * Gets a number represents the row count per page. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getRowCount * @return {Number} Returns the row count per page. * @since 1.2.0 **/ Grid.prototype.getRowCount = function() { return this.rowCount; }; /** * Gets the actual search phrase. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSearchPhrase * @return {String} Returns the actual search phrase. * @since 1.2.0 **/ Grid.prototype.getSearchPhrase = function() { return this.searchPhrase; }; /** * Gets the complete list of currently selected rows. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSelectedRows * @return {Array} Returns all selected rows. * @since 1.2.0 **/ Grid.prototype.getSelectedRows = function() { return $.merge([], this.selectedRows); }; /** * Gets the sort dictionary which represents the state of column sorting. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getSortDictionary * @return {Object} Returns the sort dictionary. * @since 1.2.0 **/ Grid.prototype.getSortDictionary = function() { return $.extend({}, this.sortDictionary); }; /** * Gets a number represents the total page count. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getTotalPageCount * @return {Number} Returns the total page count. * @since 1.2.0 **/ Grid.prototype.getTotalPageCount = function() { return this.totalPages; }; /** * Gets a number represents the total row count. * This method returns only for the first grid instance a value. * Therefore be sure that only one grid instance is catched by your selector. * * @method getTotalRowCount * @return {Number} Returns the total row count. * @since 1.2.0 **/ Grid.prototype.getTotalRowCount = function() { return this.total; }; // GRID COMMON TYPE EXTENSIONS // ============ $.fn.extend({ _bgAria: function (name, value) { return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); }, _bgBusyAria: function(busy) { return (busy == null || busy) ? this._bgAria("busy", "true") : this._bgAria("busy", "false"); }, _bgRemoveAria: function (name) { return this.removeAttr("aria-" + name); }, _bgEnableAria: function (enable) { return (enable == null || enable) ? this.removeClass("disabled")._bgAria("disabled", "false") : this.addClass("disabled")._bgAria("disabled", "true"); }, _bgEnableField: function (enable) { return (enable == null || enable) ? this.removeAttr("disabled") : this.attr("disabled", "disable"); }, _bgShowAria: function (show) { return (show == null || show) ? this.show()._bgAria("hidden", "false") : this.hide()._bgAria("hidden", "true"); }, _bgSelectAria: function (select) { return (select == null || select) ? this.addClass("active")._bgAria("selected", "true") : this.removeClass("active")._bgAria("selected", "false"); }, _bgId: function (id) { return (id) ? this.attr("id", id) : this.attr("id"); } }); if (!String.prototype.resolve) { var formatter = { "checked": function(value) { if (typeof value === "boolean") { return (value) ? "checked=\"checked\"" : ""; } return value; } }; String.prototype.resolve = function (substitutes, prefixes) { var result = this; $.each(substitutes, function (key, value) { if (value != null && typeof value !== "function") { if (typeof value === "object") { var keys = (prefixes) ? $.extend([], prefixes) : []; keys.push(key); result = result.resolve(value, keys) + ""; } else { if (formatter && formatter[key] && typeof formatter[key] === "function") { value = formatter[key](value); } key = (prefixes) ? prefixes.join(".") + "." + key : key; var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); } } }); return result; }; } if (!Array.prototype.first) { Array.prototype.first = function (condition) { for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { return item; } } return null; }; } if (!Array.prototype.contains) { Array.prototype.contains = function (condition) { for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { return true; } } return false; }; } if (!Array.prototype.page) { Array.prototype.page = function (page, size) { var skip = (page - 1) * size, end = skip + size; return (this.length > skip) ? (this.length > end) ? this.slice(skip, end) : this.slice(skip) : []; }; } if (!Array.prototype.where) { Array.prototype.where = function (condition) { var result = []; for (var i = 0; i < this.length; i++) { var item = this[i]; if (condition(item)) { result.push(item); } } return result; }; } if (!Array.prototype.propValues) { Array.prototype.propValues = function (propName) { var result = []; for (var i = 0; i < this.length; i++) { result.push(this[i][propName]); } return result; }; } // GRID PLUGIN DEFINITION // ===================== var old = $.fn.bootgrid; $.fn.bootgrid = function (option) { var args = Array.prototype.slice.call(arguments, 1), returnValue = null, elements = this.each(function (index) { var $this = $(this), instance = $this.data(namespace), options = typeof option === "object" && option; if (!instance && option === "destroy") { return; } if (!instance) { $this.data(namespace, (instance = new Grid(this, options))); init.call(instance); } if (typeof option === "string") { if (option.indexOf("get") === 0 && index === 0) { returnValue = instance[option].apply(instance, args); } else if (option.indexOf("get") !== 0) { return instance[option].apply(instance, args); } } }); return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; }; $.fn.bootgrid.Constructor = Grid; // GRID NO CONFLICT // =============== $.fn.bootgrid.noConflict = function () { $.fn.bootgrid = old; return this; }; // GRID DATA-API // ============ $("[data-toggle=\"bootgrid\"]").bootgrid(); })(jQuery, window); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootstrap-growl/bootstrap-growl.js ================================================ /* * Project: Bootstrap Growl - v2.0.1 * Description: Turns standard Bootstrap alerts into "Growl-like" notifications. * Author: Mouse0270 aka Robert McIntosh * License: MIT License * Website: https://github.com/mouse0270/bootstrap-growl */ ;(function ( $, window, document, undefined ) { // Create the defaults once var pluginName = "growl", dataKey = "plugin_" + pluginName, defaults = { element: 'body', type: "info", allow_dismiss: true, placement: { from: "top", align: "right" }, offset: 20, spacing: 10, z_index: 1031, delay: 5000, timer: 1000, url_target: '_blank', mouse_over: false, animate: { enter: 'animated fadeInDown', exit: 'animated fadeOutUp' }, onShow: null, onShown: null, onHide: null, onHidden: null, icon_type: 'class', template: '' }; // The actual plugin constructor var setDefaults = function(element, options) { defaults = $.extend(true, {}, defaults, options); }, closeAll = function(options) { if (!options) { $('[data-growl="container"]').find('[data-growl="dismiss"]').trigger('click'); }else{ $('[data-growl="container"][data-growl-position="'+options+'"]').find('[data-growl="dismiss"]').trigger('click'); } }, Plugin = function (element, content, options) { var content = { content: { message: typeof content == 'object' ? content.message : content, title: content.title ? content.title : null, icon: content.icon ? content.icon : null, url: content.url ? content.url : null } }; options = $.extend(true, {}, content, options); this.settings = $.extend(true, {}, defaults, options); plugin = this; init(options, this.settings, plugin); this.$template = $template; }, init = function (options, settings, plugin) { var base = { settings: settings, element: settings.element, template: settings.template }; if (typeof settings.offset == 'number') { settings.offset = { x: settings.offset, y: settings.offset }; } $template = buildGrowl(base); addContent($template, base.settings); placement($template, base.settings); bindControls($template, base.settings,plugin); }, buildGrowl = function(base) { var $template = $(base.settings.template); $template.addClass('alert-' + base.settings.type); $template.attr('data-growl-position', base.settings.placement.from + '-' + base.settings.placement.align); $template.find('[data-growl="dismiss"]').css('display', 'none'); $template.removeClass('alert-dismissable'); if (base.settings.allow_dismiss) { $template.addClass('alert-dismissable'); $template.find('[data-growl="dismiss"]').css('display', 'block'); } return $template; }, addContent = function($template, settings) { $template.find('[data-growl="dismiss"]').css({ 'z-index': ((settings.z_index-1) >= 1 ? (settings.z_index-1) : 1) }); if (settings.content.icon) { if (settings.icon_type.toLowerCase() == 'class') { $template.find('[data-growl="icon"]').addClass(settings.content.icon); }else{ if ($template.find('[data-growl="icon"]').is('img')) { $template.find('[data-growl="icon"]').attr('src', settings.content.icon); }else{ $template.find('[data-growl="icon"]').append(''); } } } if (settings.content.title) { $template.find('[data-growl="title"]').html(settings.content.title); } if (settings.content.message) { $template.find('[data-growl="message"]').html(settings.content.message); } if (settings.content.url) { $template.find('[data-growl="url"]').attr('href', settings.content.url).attr('target', settings.url_target); $template.find('[data-growl="url"]').css({ 'position': 'absolute', 'top': 0, 'left': 0, 'width': '100%', 'height': '100%', 'z-index': ((settings.z_index-2) >= 1 ? (settings.z_index-2) : 1) }); } }, placement = function($template, settings) { var offsetAmt = settings.offset.y, gCSS = { 'position': (settings.element === 'body' ? 'fixed' : 'absolute'), 'margin': 0, 'z-index': settings.z_index, 'display': 'inline-block' }, hasAnimation = false; $('[data-growl-position="' + settings.placement.from + '-' + settings.placement.align + '"]').each(function() { return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + $(this).outerHeight() + settings.spacing); }); gCSS[settings.placement.from] = offsetAmt + "px"; $template.css(gCSS); if (settings.onShow) { settings.onShow(event); } $(settings.element).append($template); switch (settings.placement.align) { case 'center': $template.css({ 'left': '50%', 'marginLeft': -($template.outerWidth() / 2) + 'px' }); break; case 'left': $template.css('left', settings.offset.x + 'px'); break; case 'right': $template.css('right', settings.offset.x + 'px'); break; } $template.addClass('growl-animated'); $template.one('webkitAnimationStart oanimationstart MSAnimationStart animationstart', function(event) { hasAnimation = true; }); $template.one('webkitAnimationEnd oanimationend MSAnimationEnd animationend', function(event) { if (settings.onShown) { settings.onShown(event); } }); setTimeout(function() { if (!hasAnimation) { if (settings.onShown) { settings.onShown(event); } } }, 600); }, bindControls = function($template, settings, plugin) { $template.addClass(settings.animate.enter); $template.find('[data-growl="dismiss"]').on('click', function() { plugin.close(); }); $template.on('mouseover', function(e) { $template.addClass('hovering'); }).on('mouseout', function() { $template.removeClass('hovering'); }); if (settings.delay >= 1) { $template.data('growl-delay', settings.delay); var timer = setInterval(function() { var delay = parseInt($template.data('growl-delay')) - settings.timer; if ((!$template.hasClass('hovering') && settings.mouse_over == 'pause') || settings.mouse_over != 'pause') { $template.data('growl-delay', delay); } if (delay <= 0) { clearInterval(timer); plugin.close(); } }, settings.timer); } }; // Avoid Plugin.prototype conflicts Plugin.prototype = { update: function(command, update) { switch (command) { case 'icon': if (this.settings.icon_type.toLowerCase() == 'class') { this.$template.find('[data-growl="icon"]').removeClass(this.settings.content.icon); this.$template.find('[data-growl="icon"]').addClass(update); }else{ if (this.$template.find('[data-growl="icon"]').is('img')) { this.$template.find('[data-growl="icon"]') }else{ this.$template.find('[data-growl="icon"]').find('img').attr().attr('src', update); } } break; case 'url': this.$template.find('[data-growl="url"]').attr('href', update); break; case 'type': this.$template.removeClass(function (index, css) { return (css.match (/(^|\s)alert-\S+/g) || []).join(' '); }); this.$template.addClass('alert-' + update); break; default: this.$template.find('[data-growl="' + command +'"]').html(update); } return this; }, close: function() { var base = this.$template, settings = this.settings, posX = base.css(settings.placement.from), hasAnimation = false; if (settings.onHide) { settings.onHide(event); } base.addClass(this.settings.animate.exit); base.nextAll('[data-growl-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]').each(function() { $(this).css(settings.placement.from, posX); posX = (parseInt(posX)+(settings.spacing)) + $(this).outerHeight(); }); base.one('webkitAnimationStart oanimationstart MSAnimationStart animationstart', function(event) { hasAnimation = true; }); base.one('webkitAnimationEnd oanimationend MSAnimationEnd animationend', function(event) { $(this).remove(); if (settings.onHidden) { settings.onHidden(event); } }); setTimeout(function() { if (!hasAnimation) { base.remove(); if (settings.onHidden) { settings.onHidden(event); } } }, 100); return this; } }; // A really lightweight plugin wrapper around the constructor, // preventing against multiple instantiations $.growl = function ( content, options ) { if (content == false && options.command == "closeAll") { closeAll(options.position); return false; }else if (content == false) { setDefaults(this, options); return false; } var plugin = new Plugin( this, content, options ); return plugin; }; })( jQuery, window, document ); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootstrap-wizard/jquery.bootstrap.wizard.js ================================================ /*! * jQuery twitter bootstrap wizard plugin * Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard * version 1.0 * Requires jQuery v1.3.2 or later * Supports Bootstrap 2.2.x, 2.3.x, 3.0 * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com) */ ;(function($) { var bootstrapWizardCreate = function(element, options) { var element = $(element); var obj = this; // selector skips any 'li' elements that do not contain a child with a tab data-toggle var baseItemSelector = 'li:has([data-toggle="tab"])'; var historyStack = []; // Merge options with defaults var $settings = $.extend({}, $.fn.bootstrapWizard.defaults, options); var $activeTab = null; var $navigation = null; this.rebindClick = function(selector, fn) { selector.unbind('click', fn).bind('click', fn); } this.fixNavigationButtons = function() { // Get the current active tab if(!$activeTab.length) { // Select first one $navigation.find('a:first').tab('show'); $activeTab = $navigation.find(baseItemSelector + ':first'); } // See if we're currently in the first/last then disable the previous and last buttons $($settings.previousSelector, element).toggleClass('disabled', (obj.firstIndex() >= obj.currentIndex())); $($settings.nextSelector, element).toggleClass('disabled', (obj.currentIndex() >= obj.navigationLength())); $($settings.nextSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0)); $($settings.lastSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0)); $($settings.finishSelector, element).toggleClass('hidden', (obj.currentIndex() < obj.navigationLength())); $($settings.backSelector, element).toggleClass('disabled', (historyStack.length == 0)); $($settings.backSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0)); // We are unbinding and rebinding to ensure single firing and no double-click errors obj.rebindClick($($settings.nextSelector, element), obj.next); obj.rebindClick($($settings.previousSelector, element), obj.previous); obj.rebindClick($($settings.lastSelector, element), obj.last); obj.rebindClick($($settings.firstSelector, element), obj.first); obj.rebindClick($($settings.finishSelector, element), obj.finish); obj.rebindClick($($settings.backSelector, element), obj.back); if($settings.onTabShow && typeof $settings.onTabShow === 'function' && $settings.onTabShow($activeTab, $navigation, obj.currentIndex())===false){ return false; } }; this.next = function(e) { // If we clicked the last then dont activate this if(element.hasClass('last')) { return false; } if($settings.onNext && typeof $settings.onNext === 'function' && $settings.onNext($activeTab, $navigation, obj.nextIndex())===false){ return false; } var formerIndex = obj.currentIndex(); $index = obj.nextIndex(); // Did we click the last button if($index > obj.navigationLength()) { } else { historyStack.push(formerIndex); $navigation.find(baseItemSelector + ':eq(' + $index + ') a').tab('show'); } }; this.previous = function(e) { // If we clicked the first then dont activate this if(element.hasClass('first')) { return false; } if($settings.onPrevious && typeof $settings.onPrevious === 'function' && $settings.onPrevious($activeTab, $navigation, obj.previousIndex())===false){ return false; } var formerIndex = obj.currentIndex(); $index = obj.previousIndex(); if($index < 0) { } else { historyStack.push(formerIndex); $navigation.find(baseItemSelector + ':eq(' + $index + ') a').tab('show'); } }; this.first = function (e) { if($settings.onFirst && typeof $settings.onFirst === 'function' && $settings.onFirst($activeTab, $navigation, obj.firstIndex())===false){ return false; } // If the element is disabled then we won't do anything if(element.hasClass('disabled')) { return false; } historyStack.push(obj.currentIndex()); $navigation.find(baseItemSelector + ':eq(0) a').tab('show'); }; this.last = function(e) { if($settings.onLast && typeof $settings.onLast === 'function' && $settings.onLast($activeTab, $navigation, obj.lastIndex())===false){ return false; } // If the element is disabled then we won't do anything if(element.hasClass('disabled')) { return false; } historyStack.push(obj.currentIndex()); $navigation.find(baseItemSelector + ':eq(' + obj.navigationLength() + ') a').tab('show'); }; this.finish = function (e) { if ($settings.onFinish && typeof $settings.onFinish === 'function') { $settings.onFinish($activeTab, $navigation, obj.lastIndex()); } }; this.back = function () { if (historyStack.length == 0) { return null; } var formerIndex = historyStack.pop(); if ($settings.onBack && typeof $settings.onBack === 'function' && $settings.onBack($activeTab, $navigation, formerIndex) === false) { historyStack.push(formerIndex); return false; } element.find(baseItemSelector + ':eq(' + formerIndex + ') a').tab('show'); }; this.currentIndex = function() { return $navigation.find(baseItemSelector).index($activeTab); }; this.firstIndex = function() { return 0; }; this.lastIndex = function() { return obj.navigationLength(); }; this.getIndex = function(e) { return $navigation.find(baseItemSelector).index(e); }; this.nextIndex = function() { return $navigation.find(baseItemSelector).index($activeTab) + 1; }; this.previousIndex = function() { return $navigation.find(baseItemSelector).index($activeTab) - 1; }; this.navigationLength = function() { return $navigation.find(baseItemSelector).length - 1; }; this.activeTab = function() { return $activeTab; }; this.nextTab = function() { return $navigation.find(baseItemSelector + ':eq('+(obj.currentIndex()+1)+')').length ? $navigation.find(baseItemSelector + ':eq('+(obj.currentIndex()+1)+')') : null; }; this.previousTab = function() { if(obj.currentIndex() <= 0) { return null; } return $navigation.find(baseItemSelector + ':eq('+parseInt(obj.currentIndex()-1)+')'); }; this.show = function(index) { var tabToShow = isNaN(index) ? element.find(baseItemSelector + ' a[href=#' + index + ']') : element.find(baseItemSelector + ':eq(' + index + ') a'); if (tabToShow.length > 0) { historyStack.push(obj.currentIndex()); tabToShow.tab('show'); } }; this.disable = function (index) { $navigation.find(baseItemSelector + ':eq('+index+')').addClass('disabled'); }; this.enable = function(index) { $navigation.find(baseItemSelector + ':eq('+index+')').removeClass('disabled'); }; this.hide = function(index) { $navigation.find(baseItemSelector + ':eq('+index+')').hide(); }; this.display = function(index) { $navigation.find(baseItemSelector + ':eq('+index+')').show(); }; this.remove = function(args) { var $index = args[0]; var $removeTabPane = typeof args[1] != 'undefined' ? args[1] : false; var $item = $navigation.find(baseItemSelector + ':eq('+$index+')'); // Remove the tab pane first if needed if($removeTabPane) { var $href = $item.find('a').attr('href'); $($href).remove(); } // Remove menu item $item.remove(); }; var innerTabClick = function (e) { // Get the index of the clicked tab var $ul = $navigation.find(baseItemSelector); var clickedIndex = $ul.index($(e.currentTarget).parent(baseItemSelector)); var $clickedTab = $( $ul[clickedIndex] ); if($settings.onTabClick && typeof $settings.onTabClick === 'function' && $settings.onTabClick($activeTab, $navigation, obj.currentIndex(), clickedIndex, $clickedTab)===false){ return false; } }; var innerTabShown = function (e) { // use shown instead of show to help prevent double firing $element = $(e.target).parent(); var nextTab = $navigation.find(baseItemSelector).index($element); // If it's disabled then do not change if($element.hasClass('disabled')) { return false; } if($settings.onTabChange && typeof $settings.onTabChange === 'function' && $settings.onTabChange($activeTab, $navigation, obj.currentIndex(), nextTab)===false){ return false; } $activeTab = $element; // activated tab obj.fixNavigationButtons(); }; this.resetWizard = function() { // remove the existing handlers $('a[data-toggle="tab"]', $navigation).off('click', innerTabClick); $('a[data-toggle="tab"]', $navigation).off('shown shown.bs.tab', innerTabShown); // reset elements based on current state of the DOM $navigation = element.find('ul:first', element); $activeTab = $navigation.find(baseItemSelector + '.active', element); // re-add handlers $('a[data-toggle="tab"]', $navigation).on('click', innerTabClick); $('a[data-toggle="tab"]', $navigation).on('shown shown.bs.tab', innerTabShown); obj.fixNavigationButtons(); }; $navigation = element.find('ul:first', element); $activeTab = $navigation.find(baseItemSelector + '.active', element); if(!$navigation.hasClass($settings.tabClass)) { $navigation.addClass($settings.tabClass); } // Load onInit if($settings.onInit && typeof $settings.onInit === 'function'){ $settings.onInit($activeTab, $navigation, 0); } // Load onShow if($settings.onShow && typeof $settings.onShow === 'function'){ $settings.onShow($activeTab, $navigation, obj.nextIndex()); } $('a[data-toggle="tab"]', $navigation).on('click', innerTabClick); // attach to both shown and shown.bs.tab to support Bootstrap versions 2.3.2 and 3.0.0 $('a[data-toggle="tab"]', $navigation).on('shown shown.bs.tab', innerTabShown); }; $.fn.bootstrapWizard = function(options) { //expose methods if (typeof options == 'string') { var args = Array.prototype.slice.call(arguments, 1) if(args.length === 1) { args.toString(); } return this.data('bootstrapWizard')[options](args); } return this.each(function(index){ var element = $(this); // Return early if this element already has a plugin instance if (element.data('bootstrapWizard')) return; // pass options to plugin constructor var wizard = new bootstrapWizardCreate(element, options); // Store plugin object in this element's data element.data('bootstrapWizard', wizard); // and then trigger initial change wizard.fixNavigationButtons(); }); }; // expose options $.fn.bootstrapWizard.defaults = { tabClass: 'nav nav-pills', nextSelector: '.wizard li.next', previousSelector: '.wizard li.previous', firstSelector: '.wizard li.first', lastSelector: '.wizard li.last', finishSelector: '.wizard li.finish', backSelector: '.wizard li.back', onShow: null, onInit: null, onNext: null, onPrevious: null, onLast: null, onFirst: null, onFinish: null, onBack: null, onTabChange: null, onTabClick: null, onTabShow: null }; })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower.json ================================================ { "name": "vendors", "version": "0.0.0", "authors": [], "description": "", "main": "", "moduleType": [], "license": "MIT", "homepage": "", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "animate.css": "~3.4.0", "autosize": "~3.0.6", "bootstrap-select": "silviomoreto/bootstrap-select#~1.7.2", "bootstrap-sweetalert": "~0.4.5", "bower": "*", "eonasdan-bootstrap-datetimepicker": "Eonasdan/bootstrap-datetimepicker#~4.7.14", "farbtastic": "mattfarina/farbtastic#~2.0.0-alpha.1", "flot-orderBars": "emmerich/flot-orderBars", "flot.curvedlines": "MichaelZinsmaier/CurvedLines#~1.1.1", "flot.tooltip": "~0.8.5", "fullcalendar": "~2.4.0", "install": "~1.0.4", "jquery-placeholder": "~2.1.2", "jquery.easy-pie-chart": "~2.1.6", "jquery.bootgrid": "~1.3.1", "lightgallery": "~1.1.5", "malihu-custom-scrollbar-plugin": "~3.1.0", "material-design-iconic-font": "~2.1.1", "material-shadows": "~2.0.1", "mediaelement": "johndyer/mediaelement#~2.16.4", "nouislider": "leongersen/noUiSlider#~7.0.10", "summernote": "~0.6.16", "sweetalert": "~1.0.1", "typeahead.js": "~0.11.1", "Waves": "waves#~0.7.4", "jquery": "~2.1.4", "moment": "~2.10.6", "chosen": "~1.4.2" } } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/Waves/0.7.4/waves.css ================================================ /*! * Waves v0.7.4 * http://fian.my.id/Waves * * Copyright 2014 Alfiana E. Sibuea and other contributors * Released under the MIT license * https://github.com/fians/Waves/blob/master/LICENSE */ .waves-effect { position: relative; cursor: pointer; display: inline-block; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; } .waves-effect .waves-ripple { position: absolute; border-radius: 50%; width: 100px; height: 100px; margin-top: -50px; margin-left: -50px; opacity: 0; background: rgba(0, 0, 0, 0.2); background: -webkit-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%); -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; -webkit-transition-property: -webkit-transform, opacity; -moz-transition-property: -moz-transform, opacity; -o-transition-property: -o-transform, opacity; transition-property: transform, opacity; -webkit-transform: scale(0) translate(0, 0); -moz-transform: scale(0) translate(0, 0); -ms-transform: scale(0) translate(0, 0); -o-transform: scale(0) translate(0, 0); transform: scale(0) translate(0, 0); pointer-events: none; } .waves-effect.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); background: -webkit-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -o-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: -moz-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); background: radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); } .waves-effect.waves-classic .waves-ripple { background: rgba(0, 0, 0, 0.2); } .waves-effect.waves-classic.waves-light .waves-ripple { background: rgba(255, 255, 255, 0.4); } .waves-notransition { -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important; } .waves-button, .waves-circle { -webkit-transform: translateZ(0); -moz-transform: translateZ(0); -ms-transform: translateZ(0); -o-transform: translateZ(0); transform: translateZ(0); -webkit-mask-image: -webkit-radial-gradient(circle, #ffffff 100%, #000000 100%); } .waves-button, .waves-button:hover, .waves-button:visited, .waves-button-input { white-space: nowrap; vertical-align: middle; cursor: pointer; border: none; outline: none; color: inherit; background-color: rgba(0, 0, 0, 0); font-size: 1em; line-height: 1em; text-align: center; text-decoration: none; z-index: 1; } .waves-button { padding: 0.85em 1.1em; border-radius: 0.2em; } .waves-button-input { margin: 0; padding: 0.85em 1.1em; } .waves-input-wrapper { border-radius: 0.2em; vertical-align: bottom; } .waves-input-wrapper.waves-button { padding: 0; } .waves-input-wrapper .waves-button-input { position: relative; top: 0; left: 0; z-index: 1; } .waves-circle { text-align: center; width: 2.5em; height: 2.5em; line-height: 2.5em; border-radius: 50%; } .waves-float { -webkit-mask-image: none; -webkit-box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12); -webkit-transition: all 300ms; -moz-transition: all 300ms; -o-transition: all 300ms; transition: all 300ms; } .waves-float:active { -webkit-box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3); } .waves-block { display: block; } /* Firefox Bug: link not triggered */ a.waves-effect .waves-ripple { z-index: -1; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/Waves/0.7.4/waves.js ================================================ /*! * Waves v0.7.4 * http://fian.my.id/Waves * * Copyright 2014 Alfiana E. Sibuea and other contributors * Released under the MIT license * https://github.com/fians/Waves/blob/master/LICENSE */ ;(function(window, factory) { 'use strict'; // AMD. Register as an anonymous module. Wrap in function so we have access // to root via `this`. if (typeof define === 'function' && define.amd) { define([], function() { return factory.apply(window); }); } // Node. Does not work with strict CommonJS, but only CommonJS-like // environments that support module.exports, like Node. else if (typeof exports === 'object') { module.exports = factory.call(window); } // Browser globals. else { window.Waves = factory.call(window); } })(typeof global === 'object' ? global : this, function() { 'use strict'; var Waves = Waves || {}; var $$ = document.querySelectorAll.bind(document); var toString = Object.prototype.toString; var isTouchAvailable = 'ontouchstart' in window; // Find exact position of element function isWindow(obj) { return obj !== null && obj === obj.window; } function getWindow(elem) { return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView; } function isObject(value) { var type = typeof value; return type === 'function' || type === 'object' && !!value; } function isDOMNode(obj) { return isObject(obj) && obj.nodeType > 0; } function getWavesElements(nodes) { var stringRepr = toString.call(nodes); if (stringRepr === '[object String]') { return $$(nodes); } else if (isObject(nodes) && /^\[object (HTMLCollection|NodeList|Object)\]$/.test(stringRepr) && nodes.hasOwnProperty('length')) { return nodes; } else if (isDOMNode(nodes)) { return [nodes]; } return []; } function offset(elem) { var docElem, win, box = { top: 0, left: 0 }, doc = elem && elem.ownerDocument; docElem = doc.documentElement; if (typeof elem.getBoundingClientRect !== typeof undefined) { box = elem.getBoundingClientRect(); } win = getWindow(doc); return { top: box.top + win.pageYOffset - docElem.clientTop, left: box.left + win.pageXOffset - docElem.clientLeft }; } function convertStyle(styleObj) { var style = ''; for (var prop in styleObj) { if (styleObj.hasOwnProperty(prop)) { style += (prop + ':' + styleObj[prop] + ';'); } } return style; } var Effect = { // Effect duration duration: 750, // Effect delay (check for scroll before showing effect) delay: 200, show: function(e, element, velocity) { // Disable right click if (e.button === 2) { return false; } element = element || this; // Create ripple var ripple = document.createElement('div'); ripple.className = 'waves-ripple waves-rippling'; element.appendChild(ripple); // Get click coordinate and element width var pos = offset(element); var relativeY = 0; var relativeX = 0; // Support for touch devices if('touches' in e && e.touches.length) { relativeY = (e.touches[0].pageY - pos.top); relativeX = (e.touches[0].pageX - pos.left); } //Normal case else { relativeY = (e.pageY - pos.top); relativeX = (e.pageX - pos.left); } // Support for synthetic events relativeX = relativeX >= 0 ? relativeX : 0; relativeY = relativeY >= 0 ? relativeY : 0; var scale = 'scale(' + ((element.clientWidth / 100) * 3) + ')'; var translate = 'translate(0,0)'; if (velocity) { translate = 'translate(' + (velocity.x) + 'px, ' + (velocity.y) + 'px)'; } // Attach data to element ripple.setAttribute('data-hold', Date.now()); ripple.setAttribute('data-x', relativeX); ripple.setAttribute('data-y', relativeY); ripple.setAttribute('data-scale', scale); ripple.setAttribute('data-translate', translate); // Set ripple position var rippleStyle = { top: relativeY + 'px', left: relativeX + 'px' }; ripple.classList.add('waves-notransition'); ripple.setAttribute('style', convertStyle(rippleStyle)); ripple.classList.remove('waves-notransition'); // Scale the ripple rippleStyle['-webkit-transform'] = scale + ' ' + translate; rippleStyle['-moz-transform'] = scale + ' ' + translate; rippleStyle['-ms-transform'] = scale + ' ' + translate; rippleStyle['-o-transform'] = scale + ' ' + translate; rippleStyle.transform = scale + ' ' + translate; rippleStyle.opacity = '1'; var duration = e.type === 'mousemove' ? 2500 : Effect.duration; rippleStyle['-webkit-transition-duration'] = duration + 'ms'; rippleStyle['-moz-transition-duration'] = duration + 'ms'; rippleStyle['-o-transition-duration'] = duration + 'ms'; rippleStyle['transition-duration'] = duration + 'ms'; ripple.setAttribute('style', convertStyle(rippleStyle)); }, hide: function(e, element) { element = element || this; var ripples = element.getElementsByClassName('waves-rippling'); for (var i = 0, len = ripples.length; i < len; i++) { removeRipple(e, element, ripples[i]); } } }; /** * Collection of wrapper for HTML element that only have single tag * like and */ var TagWrapper = { // Wrap tag so it can perform the effect input: function(element) { var parent = element.parentNode; // If input already have parent just pass through if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) { return; } // Put element class and style to the specified parent var wrapper = document.createElement('i'); wrapper.className = element.className + ' waves-input-wrapper'; element.className = 'waves-button-input'; // Put element as child parent.replaceChild(wrapper, element); wrapper.appendChild(element); // Apply element color and background color to wrapper var elementStyle = window.getComputedStyle(element, null); var color = elementStyle.color; var backgroundColor = elementStyle.backgroundColor; wrapper.setAttribute('style', 'color:' + color + ';background:' + backgroundColor); element.setAttribute('style', 'background-color:rgba(0,0,0,0);'); }, // Wrap tag so it can perform the effect img: function(element) { var parent = element.parentNode; // If input already have parent just pass through if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) { return; } // Put element as child var wrapper = document.createElement('i'); parent.replaceChild(wrapper, element); wrapper.appendChild(element); } }; /** * Hide the effect and remove the ripple. Must be * a separate function to pass the JSLint... */ function removeRipple(e, el, ripple) { // Check if the ripple still exist if (!ripple) { return; } ripple.classList.remove('waves-rippling'); var relativeX = ripple.getAttribute('data-x'); var relativeY = ripple.getAttribute('data-y'); var scale = ripple.getAttribute('data-scale'); var translate = ripple.getAttribute('data-translate'); // Get delay beetween mousedown and mouse leave var diff = Date.now() - Number(ripple.getAttribute('data-hold')); var delay = 350 - diff; if (delay < 0) { delay = 0; } if (e.type === 'mousemove') { delay = 150; } // Fade out ripple after delay var duration = e.type === 'mousemove' ? 2500 : Effect.duration; setTimeout(function() { var style = { top: relativeY + 'px', left: relativeX + 'px', opacity: '0', // Duration '-webkit-transition-duration': duration + 'ms', '-moz-transition-duration': duration + 'ms', '-o-transition-duration': duration + 'ms', 'transition-duration': duration + 'ms', '-webkit-transform': scale + ' ' + translate, '-moz-transform': scale + ' ' + translate, '-ms-transform': scale + ' ' + translate, '-o-transform': scale + ' ' + translate, 'transform': scale + ' ' + translate }; ripple.setAttribute('style', convertStyle(style)); setTimeout(function() { try { el.removeChild(ripple); } catch (e) { return false; } }, duration); }, delay); } /** * Disable mousedown event for 500ms during and after touch */ var TouchHandler = { /* uses an integer rather than bool so there's no issues with * needing to clear timeouts if another touch event occurred * within the 500ms. Cannot mouseup between touchstart and * touchend, nor in the 500ms after touchend. */ touches: 0, allowEvent: function(e) { var allow = true; if (/^(mousedown|mousemove)$/.test(e.type) && TouchHandler.touches) { allow = false; } return allow; }, registerEvent: function(e) { var eType = e.type; if (eType === 'touchstart') { TouchHandler.touches += 1; // push } else if (/^(touchend|touchcancel)$/.test(eType)) { setTimeout(function() { if (TouchHandler.touches) { TouchHandler.touches -= 1; // pop after 500ms } }, 500); } } }; /** * Delegated click handler for .waves-effect element. * returns null when .waves-effect element not in "click tree" */ function getWavesEffectElement(e) { if (TouchHandler.allowEvent(e) === false) { return null; } var element = null; var target = e.target || e.srcElement; while (target.parentElement !== null) { if (target.classList.contains('waves-effect') && (!(target instanceof SVGElement))) { element = target; break; } target = target.parentElement; } return element; } /** * Bubble the click and show effect if .waves-effect elem was found */ function showEffect(e) { // Disable effect if element has "disabled" property on it // In some cases, the event is not triggered by the current element // if (e.target.getAttribute('disabled') !== null) { // return; // } var element = getWavesEffectElement(e); if (element !== null) { // Make it sure the element has either disabled property, disabled attribute or 'disabled' class if (element.disabled || element.getAttribute('disabled') || element.classList.contains('disabled')) { return; } TouchHandler.registerEvent(e); if (e.type === 'touchstart' && Effect.delay) { var hidden = false; var timer = setTimeout(function () { timer = null; Effect.show(e, element); }, Effect.delay); var hideEffect = function(hideEvent) { // if touch hasn't moved, and effect not yet started: start effect now if (timer) { clearTimeout(timer); timer = null; Effect.show(e, element); } if (!hidden) { hidden = true; Effect.hide(hideEvent, element); } }; var touchMove = function(moveEvent) { if (timer) { clearTimeout(timer); timer = null; } hideEffect(moveEvent); }; element.addEventListener('touchmove', touchMove, false); element.addEventListener('touchend', hideEffect, false); element.addEventListener('touchcancel', hideEffect, false); } else { Effect.show(e, element); if (isTouchAvailable) { element.addEventListener('touchend', Effect.hide, false); element.addEventListener('touchcancel', Effect.hide, false); } element.addEventListener('mouseup', Effect.hide, false); element.addEventListener('mouseleave', Effect.hide, false); } } } Waves.init = function(options) { var body = document.body; options = options || {}; if ('duration' in options) { Effect.duration = options.duration; } if ('delay' in options) { Effect.delay = options.delay; } if (isTouchAvailable) { body.addEventListener('touchstart', showEffect, false); body.addEventListener('touchcancel', TouchHandler.registerEvent, false); body.addEventListener('touchend', TouchHandler.registerEvent, false); } body.addEventListener('mousedown', showEffect, false); }; /** * Attach Waves to dynamically loaded inputs, or add .waves-effect and other * waves classes to a set of elements. Set drag to true if the ripple mouseover * or skimming effect should be applied to the elements. */ Waves.attach = function(elements, classes) { elements = getWavesElements(elements); if (toString.call(classes) === '[object Array]') { classes = classes.join(' '); } classes = classes ? ' ' + classes : ''; var element, tagName; for (var i = 0, len = elements.length; i < len; i++) { element = elements[i]; tagName = element.tagName.toLowerCase(); if (['input', 'img'].indexOf(tagName) !== -1) { TagWrapper[tagName](element); element = element.parentElement; } if (element.className.indexOf('waves-effect') === -1) { element.className += ' waves-effect' + classes; } } }; /** * Cause a ripple to appear in an element via code. */ Waves.ripple = function(elements, options) { elements = getWavesElements(elements); var elementsLen = elements.length; options = options || {}; options.wait = options.wait || 0; options.position = options.position || null; // default = centre of element if (elementsLen) { var element, pos, off, centre = {}, i = 0; var mousedown = { type: 'mousedown', button: 1 }; var hideRipple = function(mouseup, element) { return function() { Effect.hide(mouseup, element); }; }; for (; i < elementsLen; i++) { element = elements[i]; pos = options.position || { x: element.clientWidth / 2, y: element.clientHeight / 2 }; off = offset(element); centre.x = off.left + pos.x; centre.y = off.top + pos.y; mousedown.pageX = centre.x; mousedown.pageY = centre.y; Effect.show(mousedown, element); if (options.wait >= 0 && options.wait !== null) { var mouseup = { type: 'mouseup', button: 1 }; setTimeout(hideRipple(mouseup, element), options.wait); } } } }; /** * Remove all ripples from an element. */ Waves.calm = function(elements) { elements = getWavesElements(elements); var mouseup = { type: 'mouseup', button: 1 }; for (var i = 0, len = elements.length; i < len; i++) { Effect.hide(mouseup, elements[i]); } }; /** * Deprecated API fallback */ Waves.displayEffect = function(options) { console.error('Waves.displayEffect() has been deprecated and will be removed in future version. Please use Waves.init() to initialize Waves effect'); Waves.init(options); }; return Waves; }); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/animate.css/animate.css ================================================ @charset "UTF-8"; /*! Animate.css - http://daneden.me/animate Licensed under the MIT license - http://opensource.org/licenses/MIT Copyright (c) 2015 Daniel Eden */ .animated { -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; animation-fill-mode: both; } .animated.infinite { -webkit-animation-iteration-count: infinite; animation-iteration-count: infinite; } .animated.hinge { -webkit-animation-duration: 2s; animation-duration: 2s; } .animated.bounceIn, .animated.bounceOut { -webkit-animation-duration: .75s; animation-duration: .75s; } .animated.flipOutX, .animated.flipOutY { -webkit-animation-duration: .75s; animation-duration: .75s; } @-webkit-keyframes bounce { from, 20%, 53%, 80%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } 40%, 43% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -30px, 0); transform: translate3d(0, -30px, 0); } 70% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -15px, 0); transform: translate3d(0, -15px, 0); } 90% { -webkit-transform: translate3d(0,-4px,0); transform: translate3d(0,-4px,0); } } @keyframes bounce { from, 20%, 53%, 80%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } 40%, 43% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -30px, 0); transform: translate3d(0, -30px, 0); } 70% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -15px, 0); transform: translate3d(0, -15px, 0); } 90% { -webkit-transform: translate3d(0,-4px,0); transform: translate3d(0,-4px,0); } } .bounce { -webkit-animation-name: bounce; animation-name: bounce; -webkit-transform-origin: center bottom; transform-origin: center bottom; } @-webkit-keyframes flash { from, 50%, 100% { opacity: 1; } 25%, 75% { opacity: 0; } } @keyframes flash { from, 50%, 100% { opacity: 1; } 25%, 75% { opacity: 0; } } .flash { -webkit-animation-name: flash; animation-name: flash; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes pulse { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 50% { -webkit-transform: scale3d(1.05, 1.05, 1.05); transform: scale3d(1.05, 1.05, 1.05); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes pulse { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 50% { -webkit-transform: scale3d(1.05, 1.05, 1.05); transform: scale3d(1.05, 1.05, 1.05); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .pulse { -webkit-animation-name: pulse; animation-name: pulse; } @-webkit-keyframes rubberBand { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 30% { -webkit-transform: scale3d(1.25, 0.75, 1); transform: scale3d(1.25, 0.75, 1); } 40% { -webkit-transform: scale3d(0.75, 1.25, 1); transform: scale3d(0.75, 1.25, 1); } 50% { -webkit-transform: scale3d(1.15, 0.85, 1); transform: scale3d(1.15, 0.85, 1); } 65% { -webkit-transform: scale3d(.95, 1.05, 1); transform: scale3d(.95, 1.05, 1); } 75% { -webkit-transform: scale3d(1.05, .95, 1); transform: scale3d(1.05, .95, 1); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes rubberBand { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 30% { -webkit-transform: scale3d(1.25, 0.75, 1); transform: scale3d(1.25, 0.75, 1); } 40% { -webkit-transform: scale3d(0.75, 1.25, 1); transform: scale3d(0.75, 1.25, 1); } 50% { -webkit-transform: scale3d(1.15, 0.85, 1); transform: scale3d(1.15, 0.85, 1); } 65% { -webkit-transform: scale3d(.95, 1.05, 1); transform: scale3d(.95, 1.05, 1); } 75% { -webkit-transform: scale3d(1.05, .95, 1); transform: scale3d(1.05, .95, 1); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .rubberBand { -webkit-animation-name: rubberBand; animation-name: rubberBand; } @-webkit-keyframes shake { from, 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } } @keyframes shake { from, 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } } .shake { -webkit-animation-name: shake; animation-name: shake; } @-webkit-keyframes swing { 20% { -webkit-transform: rotate3d(0, 0, 1, 15deg); transform: rotate3d(0, 0, 1, 15deg); } 40% { -webkit-transform: rotate3d(0, 0, 1, -10deg); transform: rotate3d(0, 0, 1, -10deg); } 60% { -webkit-transform: rotate3d(0, 0, 1, 5deg); transform: rotate3d(0, 0, 1, 5deg); } 80% { -webkit-transform: rotate3d(0, 0, 1, -5deg); transform: rotate3d(0, 0, 1, -5deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } @keyframes swing { 20% { -webkit-transform: rotate3d(0, 0, 1, 15deg); transform: rotate3d(0, 0, 1, 15deg); } 40% { -webkit-transform: rotate3d(0, 0, 1, -10deg); transform: rotate3d(0, 0, 1, -10deg); } 60% { -webkit-transform: rotate3d(0, 0, 1, 5deg); transform: rotate3d(0, 0, 1, 5deg); } 80% { -webkit-transform: rotate3d(0, 0, 1, -5deg); transform: rotate3d(0, 0, 1, -5deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } .swing { -webkit-transform-origin: top center; transform-origin: top center; -webkit-animation-name: swing; animation-name: swing; } @-webkit-keyframes tada { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 10%, 20% { -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); } 30%, 50%, 70%, 90% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); } 40%, 60%, 80% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes tada { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 10%, 20% { -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); } 30%, 50%, 70%, 90% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); } 40%, 60%, 80% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .tada { -webkit-animation-name: tada; animation-name: tada; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes wobble { from { -webkit-transform: none; transform: none; } 15% { -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); } 30% { -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); } 45% { -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); } 60% { -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); } 75% { -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); } 100% { -webkit-transform: none; transform: none; } } @keyframes wobble { from { -webkit-transform: none; transform: none; } 15% { -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); } 30% { -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); } 45% { -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); } 60% { -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); } 75% { -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); } 100% { -webkit-transform: none; transform: none; } } .wobble { -webkit-animation-name: wobble; animation-name: wobble; } @-webkit-keyframes jello { from, 11.1%, 100% { -webkit-transform: none; transform: none; } 22.2% { -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); transform: skewX(-12.5deg) skewY(-12.5deg); } 33.3% { -webkit-transform: skewX(6.25deg) skewY(6.25deg); transform: skewX(6.25deg) skewY(6.25deg); } 44.4% { -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); transform: skewX(-3.125deg) skewY(-3.125deg); } 55.5% { -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); transform: skewX(1.5625deg) skewY(1.5625deg); } 66.6% { -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); transform: skewX(-0.78125deg) skewY(-0.78125deg); } 77.7% { -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); transform: skewX(0.390625deg) skewY(0.390625deg); } 88.8% { -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); transform: skewX(-0.1953125deg) skewY(-0.1953125deg); } } @keyframes jello { from, 11.1%, 100% { -webkit-transform: none; transform: none; } 22.2% { -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); transform: skewX(-12.5deg) skewY(-12.5deg); } 33.3% { -webkit-transform: skewX(6.25deg) skewY(6.25deg); transform: skewX(6.25deg) skewY(6.25deg); } 44.4% { -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); transform: skewX(-3.125deg) skewY(-3.125deg); } 55.5% { -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); transform: skewX(1.5625deg) skewY(1.5625deg); } 66.6% { -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); transform: skewX(-0.78125deg) skewY(-0.78125deg); } 77.7% { -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); transform: skewX(0.390625deg) skewY(0.390625deg); } 88.8% { -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); transform: skewX(-0.1953125deg) skewY(-0.1953125deg); } } .jello { -webkit-animation-name: jello; animation-name: jello; -webkit-transform-origin: center; transform-origin: center; } @-webkit-keyframes bounceIn { from, 20%, 40%, 60%, 80%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 20% { -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 40% { -webkit-transform: scale3d(.9, .9, .9); transform: scale3d(.9, .9, .9); } 60% { opacity: 1; -webkit-transform: scale3d(1.03, 1.03, 1.03); transform: scale3d(1.03, 1.03, 1.03); } 80% { -webkit-transform: scale3d(.97, .97, .97); transform: scale3d(.97, .97, .97); } 100% { opacity: 1; -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes bounceIn { from, 20%, 40%, 60%, 80%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 20% { -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 40% { -webkit-transform: scale3d(.9, .9, .9); transform: scale3d(.9, .9, .9); } 60% { opacity: 1; -webkit-transform: scale3d(1.03, 1.03, 1.03); transform: scale3d(1.03, 1.03, 1.03); } 80% { -webkit-transform: scale3d(.97, .97, .97); transform: scale3d(.97, .97, .97); } 100% { opacity: 1; -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .bounceIn { -webkit-animation-name: bounceIn; animation-name: bounceIn; } @-webkit-keyframes bounceInDown { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0); transform: translate3d(0, -3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0); transform: translate3d(0, 25px, 0); } 75% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 90% { -webkit-transform: translate3d(0, 5px, 0); transform: translate3d(0, 5px, 0); } 100% { -webkit-transform: none; transform: none; } } @keyframes bounceInDown { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0); transform: translate3d(0, -3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0); transform: translate3d(0, 25px, 0); } 75% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 90% { -webkit-transform: translate3d(0, 5px, 0); transform: translate3d(0, 5px, 0); } 100% { -webkit-transform: none; transform: none; } } .bounceInDown { -webkit-animation-name: bounceInDown; animation-name: bounceInDown; } @-webkit-keyframes bounceInLeft { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(-3000px, 0, 0); transform: translate3d(-3000px, 0, 0); } 60% { opacity: 1; -webkit-transform: translate3d(25px, 0, 0); transform: translate3d(25px, 0, 0); } 75% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 90% { -webkit-transform: translate3d(5px, 0, 0); transform: translate3d(5px, 0, 0); } 100% { -webkit-transform: none; transform: none; } } @keyframes bounceInLeft { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(-3000px, 0, 0); transform: translate3d(-3000px, 0, 0); } 60% { opacity: 1; -webkit-transform: translate3d(25px, 0, 0); transform: translate3d(25px, 0, 0); } 75% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 90% { -webkit-transform: translate3d(5px, 0, 0); transform: translate3d(5px, 0, 0); } 100% { -webkit-transform: none; transform: none; } } .bounceInLeft { -webkit-animation-name: bounceInLeft; animation-name: bounceInLeft; } @-webkit-keyframes bounceInRight { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } from { opacity: 0; -webkit-transform: translate3d(3000px, 0, 0); transform: translate3d(3000px, 0, 0); } 60% { opacity: 1; -webkit-transform: translate3d(-25px, 0, 0); transform: translate3d(-25px, 0, 0); } 75% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } 90% { -webkit-transform: translate3d(-5px, 0, 0); transform: translate3d(-5px, 0, 0); } 100% { -webkit-transform: none; transform: none; } } @keyframes bounceInRight { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } from { opacity: 0; -webkit-transform: translate3d(3000px, 0, 0); transform: translate3d(3000px, 0, 0); } 60% { opacity: 1; -webkit-transform: translate3d(-25px, 0, 0); transform: translate3d(-25px, 0, 0); } 75% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } 90% { -webkit-transform: translate3d(-5px, 0, 0); transform: translate3d(-5px, 0, 0); } 100% { -webkit-transform: none; transform: none; } } .bounceInRight { -webkit-animation-name: bounceInRight; animation-name: bounceInRight; } @-webkit-keyframes bounceInUp { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } from { opacity: 0; -webkit-transform: translate3d(0, 3000px, 0); transform: translate3d(0, 3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 75% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } 90% { -webkit-transform: translate3d(0, -5px, 0); transform: translate3d(0, -5px, 0); } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes bounceInUp { from, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } from { opacity: 0; -webkit-transform: translate3d(0, 3000px, 0); transform: translate3d(0, 3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 75% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } 90% { -webkit-transform: translate3d(0, -5px, 0); transform: translate3d(0, -5px, 0); } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .bounceInUp { -webkit-animation-name: bounceInUp; animation-name: bounceInUp; } @-webkit-keyframes bounceOut { 20% { -webkit-transform: scale3d(.9, .9, .9); transform: scale3d(.9, .9, .9); } 50%, 55% { opacity: 1; -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 100% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } } @keyframes bounceOut { 20% { -webkit-transform: scale3d(.9, .9, .9); transform: scale3d(.9, .9, .9); } 50%, 55% { opacity: 1; -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 100% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } } .bounceOut { -webkit-animation-name: bounceOut; animation-name: bounceOut; } @-webkit-keyframes bounceOutDown { 20% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 100% { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } @keyframes bounceOutDown { 20% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 100% { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } .bounceOutDown { -webkit-animation-name: bounceOutDown; animation-name: bounceOutDown; } @-webkit-keyframes bounceOutLeft { 20% { opacity: 1; -webkit-transform: translate3d(20px, 0, 0); transform: translate3d(20px, 0, 0); } 100% { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } @keyframes bounceOutLeft { 20% { opacity: 1; -webkit-transform: translate3d(20px, 0, 0); transform: translate3d(20px, 0, 0); } 100% { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } .bounceOutLeft { -webkit-animation-name: bounceOutLeft; animation-name: bounceOutLeft; } @-webkit-keyframes bounceOutRight { 20% { opacity: 1; -webkit-transform: translate3d(-20px, 0, 0); transform: translate3d(-20px, 0, 0); } 100% { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } @keyframes bounceOutRight { 20% { opacity: 1; -webkit-transform: translate3d(-20px, 0, 0); transform: translate3d(-20px, 0, 0); } 100% { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } .bounceOutRight { -webkit-animation-name: bounceOutRight; animation-name: bounceOutRight; } @-webkit-keyframes bounceOutUp { 20% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, 20px, 0); transform: translate3d(0, 20px, 0); } 100% { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } @keyframes bounceOutUp { 20% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, 20px, 0); transform: translate3d(0, 20px, 0); } 100% { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } .bounceOutUp { -webkit-animation-name: bounceOutUp; animation-name: bounceOutUp; } @-webkit-keyframes fadeIn { from { opacity: 0; } 100% { opacity: 1; } } @keyframes fadeIn { from { opacity: 0; } 100% { opacity: 1; } } .fadeIn { -webkit-animation-name: fadeIn; animation-name: fadeIn; } @-webkit-keyframes fadeInDown { from { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInDown { from { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInDown { -webkit-animation-name: fadeInDown; animation-name: fadeInDown; } @-webkit-keyframes fadeInDownBig { from { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInDownBig { from { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInDownBig { -webkit-animation-name: fadeInDownBig; animation-name: fadeInDownBig; } @-webkit-keyframes fadeInLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInLeft { -webkit-animation-name: fadeInLeft; animation-name: fadeInLeft; } @-webkit-keyframes fadeInLeftBig { from { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInLeftBig { from { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInLeftBig { -webkit-animation-name: fadeInLeftBig; animation-name: fadeInLeftBig; } @-webkit-keyframes fadeInRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInRight { -webkit-animation-name: fadeInRight; animation-name: fadeInRight; } @-webkit-keyframes fadeInRightBig { from { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInRightBig { from { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInRightBig { -webkit-animation-name: fadeInRightBig; animation-name: fadeInRightBig; } @-webkit-keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInUp { -webkit-animation-name: fadeInUp; animation-name: fadeInUp; } @-webkit-keyframes fadeInUpBig { from { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInUpBig { from { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .fadeInUpBig { -webkit-animation-name: fadeInUpBig; animation-name: fadeInUpBig; } @-webkit-keyframes fadeOut { from { opacity: 1; } 100% { opacity: 0; } } @keyframes fadeOut { from { opacity: 1; } 100% { opacity: 0; } } .fadeOut { -webkit-animation-name: fadeOut; animation-name: fadeOut; } @-webkit-keyframes fadeOutDown { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } @keyframes fadeOutDown { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } .fadeOutDown { -webkit-animation-name: fadeOutDown; animation-name: fadeOutDown; } @-webkit-keyframes fadeOutDownBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } @keyframes fadeOutDownBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } .fadeOutDownBig { -webkit-animation-name: fadeOutDownBig; animation-name: fadeOutDownBig; } @-webkit-keyframes fadeOutLeft { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } @keyframes fadeOutLeft { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } .fadeOutLeft { -webkit-animation-name: fadeOutLeft; animation-name: fadeOutLeft; } @-webkit-keyframes fadeOutLeftBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } @keyframes fadeOutLeftBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } .fadeOutLeftBig { -webkit-animation-name: fadeOutLeftBig; animation-name: fadeOutLeftBig; } @-webkit-keyframes fadeOutRight { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } @keyframes fadeOutRight { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } .fadeOutRight { -webkit-animation-name: fadeOutRight; animation-name: fadeOutRight; } @-webkit-keyframes fadeOutRightBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } @keyframes fadeOutRightBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } .fadeOutRightBig { -webkit-animation-name: fadeOutRightBig; animation-name: fadeOutRightBig; } @-webkit-keyframes fadeOutUp { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } @keyframes fadeOutUp { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } .fadeOutUp { -webkit-animation-name: fadeOutUp; animation-name: fadeOutUp; } @-webkit-keyframes fadeOutUpBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } @keyframes fadeOutUpBig { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } .fadeOutUpBig { -webkit-animation-name: fadeOutUpBig; animation-name: fadeOutUpBig; } @-webkit-keyframes flip { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); transform: perspective(400px) rotate3d(0, 1, 0, -360deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 40% { -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 50% { -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 80% { -webkit-transform: perspective(400px) scale3d(.95, .95, .95); transform: perspective(400px) scale3d(.95, .95, .95); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } } @keyframes flip { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); transform: perspective(400px) rotate3d(0, 1, 0, -360deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 40% { -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 50% { -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 80% { -webkit-transform: perspective(400px) scale3d(.95, .95, .95); transform: perspective(400px) scale3d(.95, .95, .95); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } } .animated.flip { -webkit-backface-visibility: visible; backface-visibility: visible; -webkit-animation-name: flip; animation-name: flip; } @-webkit-keyframes flipInX { from { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); } } @keyframes flipInX { from { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); } } .flipInX { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipInX; animation-name: flipInX; } @-webkit-keyframes flipInY { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); transform: perspective(400px) rotate3d(0, 1, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); transform: perspective(400px) rotate3d(0, 1, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); transform: perspective(400px) rotate3d(0, 1, 0, -5deg); } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); } } @keyframes flipInY { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); transform: perspective(400px) rotate3d(0, 1, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); transform: perspective(400px) rotate3d(0, 1, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); transform: perspective(400px) rotate3d(0, 1, 0, -5deg); } 100% { -webkit-transform: perspective(400px); transform: perspective(400px); } } .flipInY { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipInY; animation-name: flipInY; } @-webkit-keyframes flipOutX { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); opacity: 1; } 100% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); opacity: 0; } } @keyframes flipOutX { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); opacity: 1; } 100% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); opacity: 0; } } .flipOutX { -webkit-animation-name: flipOutX; animation-name: flipOutX; -webkit-backface-visibility: visible !important; backface-visibility: visible !important; } @-webkit-keyframes flipOutY { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); transform: perspective(400px) rotate3d(0, 1, 0, -15deg); opacity: 1; } 100% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); opacity: 0; } } @keyframes flipOutY { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); transform: perspective(400px) rotate3d(0, 1, 0, -15deg); opacity: 1; } 100% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); opacity: 0; } } .flipOutY { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipOutY; animation-name: flipOutY; } @-webkit-keyframes lightSpeedIn { from { -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); transform: translate3d(100%, 0, 0) skewX(-30deg); opacity: 0; } 60% { -webkit-transform: skewX(20deg); transform: skewX(20deg); opacity: 1; } 80% { -webkit-transform: skewX(-5deg); transform: skewX(-5deg); opacity: 1; } 100% { -webkit-transform: none; transform: none; opacity: 1; } } @keyframes lightSpeedIn { from { -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); transform: translate3d(100%, 0, 0) skewX(-30deg); opacity: 0; } 60% { -webkit-transform: skewX(20deg); transform: skewX(20deg); opacity: 1; } 80% { -webkit-transform: skewX(-5deg); transform: skewX(-5deg); opacity: 1; } 100% { -webkit-transform: none; transform: none; opacity: 1; } } .lightSpeedIn { -webkit-animation-name: lightSpeedIn; animation-name: lightSpeedIn; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } @-webkit-keyframes lightSpeedOut { from { opacity: 1; } 100% { -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); transform: translate3d(100%, 0, 0) skewX(30deg); opacity: 0; } } @keyframes lightSpeedOut { from { opacity: 1; } 100% { -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); transform: translate3d(100%, 0, 0) skewX(30deg); opacity: 0; } } .lightSpeedOut { -webkit-animation-name: lightSpeedOut; animation-name: lightSpeedOut; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } @-webkit-keyframes rotateIn { from { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: rotate3d(0, 0, 1, -200deg); transform: rotate3d(0, 0, 1, -200deg); opacity: 0; } 100% { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: none; transform: none; opacity: 1; } } @keyframes rotateIn { from { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: rotate3d(0, 0, 1, -200deg); transform: rotate3d(0, 0, 1, -200deg); opacity: 0; } 100% { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: none; transform: none; opacity: 1; } } .rotateIn { -webkit-animation-name: rotateIn; animation-name: rotateIn; } @-webkit-keyframes rotateInDownLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: none; transform: none; opacity: 1; } } @keyframes rotateInDownLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: none; transform: none; opacity: 1; } } .rotateInDownLeft { -webkit-animation-name: rotateInDownLeft; animation-name: rotateInDownLeft; } @-webkit-keyframes rotateInDownRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: none; transform: none; opacity: 1; } } @keyframes rotateInDownRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: none; transform: none; opacity: 1; } } .rotateInDownRight { -webkit-animation-name: rotateInDownRight; animation-name: rotateInDownRight; } @-webkit-keyframes rotateInUpLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: none; transform: none; opacity: 1; } } @keyframes rotateInUpLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: none; transform: none; opacity: 1; } } .rotateInUpLeft { -webkit-animation-name: rotateInUpLeft; animation-name: rotateInUpLeft; } @-webkit-keyframes rotateInUpRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, -90deg); transform: rotate3d(0, 0, 1, -90deg); opacity: 0; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: none; transform: none; opacity: 1; } } @keyframes rotateInUpRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, -90deg); transform: rotate3d(0, 0, 1, -90deg); opacity: 0; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: none; transform: none; opacity: 1; } } .rotateInUpRight { -webkit-animation-name: rotateInUpRight; animation-name: rotateInUpRight; } @-webkit-keyframes rotateOut { from { -webkit-transform-origin: center; transform-origin: center; opacity: 1; } 100% { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: rotate3d(0, 0, 1, 200deg); transform: rotate3d(0, 0, 1, 200deg); opacity: 0; } } @keyframes rotateOut { from { -webkit-transform-origin: center; transform-origin: center; opacity: 1; } 100% { -webkit-transform-origin: center; transform-origin: center; -webkit-transform: rotate3d(0, 0, 1, 200deg); transform: rotate3d(0, 0, 1, 200deg); opacity: 0; } } .rotateOut { -webkit-animation-name: rotateOut; animation-name: rotateOut; } @-webkit-keyframes rotateOutDownLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; opacity: 1; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } } @keyframes rotateOutDownLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; opacity: 1; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } } .rotateOutDownLeft { -webkit-animation-name: rotateOutDownLeft; animation-name: rotateOutDownLeft; } @-webkit-keyframes rotateOutDownRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; opacity: 1; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } @keyframes rotateOutDownRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; opacity: 1; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } .rotateOutDownRight { -webkit-animation-name: rotateOutDownRight; animation-name: rotateOutDownRight; } @-webkit-keyframes rotateOutUpLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; opacity: 1; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } @keyframes rotateOutUpLeft { from { -webkit-transform-origin: left bottom; transform-origin: left bottom; opacity: 1; } 100% { -webkit-transform-origin: left bottom; transform-origin: left bottom; -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } .rotateOutUpLeft { -webkit-animation-name: rotateOutUpLeft; animation-name: rotateOutUpLeft; } @-webkit-keyframes rotateOutUpRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; opacity: 1; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, 90deg); transform: rotate3d(0, 0, 1, 90deg); opacity: 0; } } @keyframes rotateOutUpRight { from { -webkit-transform-origin: right bottom; transform-origin: right bottom; opacity: 1; } 100% { -webkit-transform-origin: right bottom; transform-origin: right bottom; -webkit-transform: rotate3d(0, 0, 1, 90deg); transform: rotate3d(0, 0, 1, 90deg); opacity: 0; } } .rotateOutUpRight { -webkit-animation-name: rotateOutUpRight; animation-name: rotateOutUpRight; } @-webkit-keyframes hinge { 0% { -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 20%, 60% { -webkit-transform: rotate3d(0, 0, 1, 80deg); transform: rotate3d(0, 0, 1, 80deg); -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 40%, 80% { -webkit-transform: rotate3d(0, 0, 1, 60deg); transform: rotate3d(0, 0, 1, 60deg); -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; opacity: 1; } 100% { -webkit-transform: translate3d(0, 700px, 0); transform: translate3d(0, 700px, 0); opacity: 0; } } @keyframes hinge { 0% { -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 20%, 60% { -webkit-transform: rotate3d(0, 0, 1, 80deg); transform: rotate3d(0, 0, 1, 80deg); -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 40%, 80% { -webkit-transform: rotate3d(0, 0, 1, 60deg); transform: rotate3d(0, 0, 1, 60deg); -webkit-transform-origin: top left; transform-origin: top left; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; opacity: 1; } 100% { -webkit-transform: translate3d(0, 700px, 0); transform: translate3d(0, 700px, 0); opacity: 0; } } .hinge { -webkit-animation-name: hinge; animation-name: hinge; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes rollIn { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes rollIn { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); } 100% { opacity: 1; -webkit-transform: none; transform: none; } } .rollIn { -webkit-animation-name: rollIn; animation-name: rollIn; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes rollOut { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } @keyframes rollOut { from { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } .rollOut { -webkit-animation-name: rollOut; animation-name: rollOut; } @-webkit-keyframes zoomIn { from { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 50% { opacity: 1; } } @keyframes zoomIn { from { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 50% { opacity: 1; } } .zoomIn { -webkit-animation-name: zoomIn; animation-name: zoomIn; } @-webkit-keyframes zoomInDown { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomInDown { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomInDown { -webkit-animation-name: zoomInDown; animation-name: zoomInDown; } @-webkit-keyframes zoomInLeft { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomInLeft { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomInLeft { -webkit-animation-name: zoomInLeft; animation-name: zoomInLeft; } @-webkit-keyframes zoomInRight { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomInRight { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomInRight { -webkit-animation-name: zoomInRight; animation-name: zoomInRight; } @-webkit-keyframes zoomInUp { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomInUp { from { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 60% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomInUp { -webkit-animation-name: zoomInUp; animation-name: zoomInUp; } @-webkit-keyframes zoomOut { from { opacity: 1; } 50% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 100% { opacity: 0; } } @keyframes zoomOut { from { opacity: 1; } 50% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 100% { opacity: 0; } } .zoomOut { -webkit-animation-name: zoomOut; animation-name: zoomOut; } @-webkit-keyframes zoomOutDown { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 100% { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); -webkit-transform-origin: center bottom; transform-origin: center bottom; -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomOutDown { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 100% { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); -webkit-transform-origin: center bottom; transform-origin: center bottom; -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomOutDown { -webkit-animation-name: zoomOutDown; animation-name: zoomOutDown; } @-webkit-keyframes zoomOutLeft { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); } 100% { opacity: 0; -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); transform: scale(.1) translate3d(-2000px, 0, 0); -webkit-transform-origin: left center; transform-origin: left center; } } @keyframes zoomOutLeft { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); } 100% { opacity: 0; -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); transform: scale(.1) translate3d(-2000px, 0, 0); -webkit-transform-origin: left center; transform-origin: left center; } } .zoomOutLeft { -webkit-animation-name: zoomOutLeft; animation-name: zoomOutLeft; } @-webkit-keyframes zoomOutRight { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); } 100% { opacity: 0; -webkit-transform: scale(.1) translate3d(2000px, 0, 0); transform: scale(.1) translate3d(2000px, 0, 0); -webkit-transform-origin: right center; transform-origin: right center; } } @keyframes zoomOutRight { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); } 100% { opacity: 0; -webkit-transform: scale(.1) translate3d(2000px, 0, 0); transform: scale(.1) translate3d(2000px, 0, 0); -webkit-transform-origin: right center; transform-origin: right center; } } .zoomOutRight { -webkit-animation-name: zoomOutRight; animation-name: zoomOutRight; } @-webkit-keyframes zoomOutUp { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 100% { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); -webkit-transform-origin: center bottom; transform-origin: center bottom; -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } @keyframes zoomOutUp { 40% { opacity: 1; -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); } 100% { opacity: 0; -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); -webkit-transform-origin: center bottom; transform-origin: center bottom; -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); } } .zoomOutUp { -webkit-animation-name: zoomOutUp; animation-name: zoomOutUp; } @-webkit-keyframes slideInDown { from { -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInDown { from { -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .slideInDown { -webkit-animation-name: slideInDown; animation-name: slideInDown; } @-webkit-keyframes slideInLeft { from { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInLeft { from { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .slideInLeft { -webkit-animation-name: slideInLeft; animation-name: slideInLeft; } @-webkit-keyframes slideInRight { from { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInRight { from { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .slideInRight { -webkit-animation-name: slideInRight; animation-name: slideInRight; } @-webkit-keyframes slideInUp { from { -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInUp { from { -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); visibility: visible; } 100% { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .slideInUp { -webkit-animation-name: slideInUp; animation-name: slideInUp; } @-webkit-keyframes slideOutDown { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } @keyframes slideOutDown { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } .slideOutDown { -webkit-animation-name: slideOutDown; animation-name: slideOutDown; } @-webkit-keyframes slideOutLeft { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } @keyframes slideOutLeft { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } .slideOutLeft { -webkit-animation-name: slideOutLeft; animation-name: slideOutLeft; } @-webkit-keyframes slideOutRight { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } @keyframes slideOutRight { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } .slideOutRight { -webkit-animation-name: slideOutRight; animation-name: slideOutRight; } @-webkit-keyframes slideOutUp { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } @keyframes slideOutUp { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 100% { visibility: hidden; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } .slideOutUp { -webkit-animation-name: slideOutUp; animation-name: slideOutUp; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/autosize/3.0.5/autosize.js ================================================ /*! Autosize 3.0.5 license: MIT http://www.jacklmoore.com/autosize */ (function (global, factory) { if (typeof define === 'function' && define.amd) { define(['exports', 'module'], factory); } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { factory(exports, module); } else { var mod = { exports: {} }; factory(mod.exports, mod); global.autosize = mod.exports; } })(this, function (exports, module) { 'use strict'; function assign(ta) { var _ref = arguments[1] === undefined ? {} : arguments[1]; var _ref$setOverflowX = _ref.setOverflowX; var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX; var _ref$setOverflowY = _ref.setOverflowY; var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY; if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || ta.hasAttribute('data-autosize-on')) return; var heightOffset = null; var overflowY = 'hidden'; function init() { var style = window.getComputedStyle(ta, null); if (style.resize === 'vertical') { ta.style.resize = 'none'; } else if (style.resize === 'both') { ta.style.resize = 'horizontal'; } if (style.boxSizing === 'content-box') { heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)); } else { heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); } update(); } function changeOverflow(value) { { // Chrome/Safari-specific fix: // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space // made available by removing the scrollbar. The following forces the necessary text reflow. var width = ta.style.width; ta.style.width = '0px'; // Force reflow: /* jshint ignore:start */ ta.offsetWidth; /* jshint ignore:end */ ta.style.width = width; } overflowY = value; if (setOverflowY) { ta.style.overflowY = value; } update(); } function update() { var startHeight = ta.style.height; var htmlTop = document.documentElement.scrollTop; var bodyTop = document.body.scrollTop; var originalHeight = ta.style.height; ta.style.height = 'auto'; var endHeight = ta.scrollHeight + heightOffset; if (ta.scrollHeight === 0) { // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. ta.style.height = originalHeight; return; } ta.style.height = endHeight + 'px'; // prevents scroll-position jumping document.documentElement.scrollTop = htmlTop; document.body.scrollTop = bodyTop; var style = window.getComputedStyle(ta, null); if (style.height !== ta.style.height) { if (overflowY !== 'visible') { changeOverflow('visible'); return; } } else { if (overflowY !== 'hidden') { changeOverflow('hidden'); return; } } if (startHeight !== ta.style.height) { var evt = document.createEvent('Event'); evt.initEvent('autosize:resized', true, false); ta.dispatchEvent(evt); } } var destroy = (function (style) { window.removeEventListener('resize', update); ta.removeEventListener('input', update); ta.removeEventListener('keyup', update); ta.removeAttribute('data-autosize-on'); ta.removeEventListener('autosize:destroy', destroy); Object.keys(style).forEach(function (key) { ta.style[key] = style[key]; }); }).bind(ta, { height: ta.style.height, resize: ta.style.resize, overflowY: ta.style.overflowY, overflowX: ta.style.overflowX, wordWrap: ta.style.wordWrap }); ta.addEventListener('autosize:destroy', destroy); // IE9 does not fire onpropertychange or oninput for deletions, // so binding to onkeyup to catch most of those events. // There is no way that I know of to detect something like 'cut' in IE9. if ('onpropertychange' in ta && 'oninput' in ta) { ta.addEventListener('keyup', update); } window.addEventListener('resize', update); ta.addEventListener('input', update); ta.addEventListener('autosize:update', update); ta.setAttribute('data-autosize-on', true); if (setOverflowY) { ta.style.overflowY = 'hidden'; } if (setOverflowX) { ta.style.overflowX = 'hidden'; ta.style.wordWrap = 'break-word'; } init(); } function destroy(ta) { if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; var evt = document.createEvent('Event'); evt.initEvent('autosize:destroy', true, false); ta.dispatchEvent(evt); } function update(ta) { if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; var evt = document.createEvent('Event'); evt.initEvent('autosize:update', true, false); ta.dispatchEvent(evt); } var autosize = null; // Do nothing in Node.js environment and IE8 (or lower) if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') { autosize = function (el) { return el; }; autosize.destroy = function (el) { return el; }; autosize.update = function (el) { return el; }; } else { autosize = function (el, options) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], function (x) { return assign(x, options); }); } return el; }; autosize.destroy = function (el) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], destroy); } return el; }; autosize.update = function (el) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], update); } return el; }; } module.exports = autosize; }); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap/3.3.6/css/bootstrap-theme.css ================================================ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ .btn-default, .btn-primary, .btn-success, .btn-info, .btn-warning, .btn-danger { text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); } .btn-default:active, .btn-primary:active, .btn-success:active, .btn-info:active, .btn-warning:active, .btn-danger:active, .btn-default.active, .btn-primary.active, .btn-success.active, .btn-info.active, .btn-warning.active, .btn-danger.active { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn-default.disabled, .btn-primary.disabled, .btn-success.disabled, .btn-info.disabled, .btn-warning.disabled, .btn-danger.disabled, .btn-default[disabled], .btn-primary[disabled], .btn-success[disabled], .btn-info[disabled], .btn-warning[disabled], .btn-danger[disabled], fieldset[disabled] .btn-default, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-success, fieldset[disabled] .btn-info, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-danger { -webkit-box-shadow: none; box-shadow: none; } .btn-default .badge, .btn-primary .badge, .btn-success .badge, .btn-info .badge, .btn-warning .badge, .btn-danger .badge { text-shadow: none; } .btn:active, .btn.active { background-image: none; } .btn-default { text-shadow: 0 1px 0 #fff; background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #dbdbdb; border-color: #ccc; } .btn-default:hover, .btn-default:focus { background-color: #e0e0e0; background-position: 0 -15px; } .btn-default:active, .btn-default.active { background-color: #e0e0e0; border-color: #dbdbdb; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #e0e0e0; background-image: none; } .btn-primary { background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #245580; } .btn-primary:hover, .btn-primary:focus { background-color: #265a88; background-position: 0 -15px; } .btn-primary:active, .btn-primary.active { background-color: #265a88; border-color: #245580; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #265a88; background-image: none; } .btn-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #3e8f3e; } .btn-success:hover, .btn-success:focus { background-color: #419641; background-position: 0 -15px; } .btn-success:active, .btn-success.active { background-color: #419641; border-color: #3e8f3e; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #419641; background-image: none; } .btn-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #28a4c9; } .btn-info:hover, .btn-info:focus { background-color: #2aabd2; background-position: 0 -15px; } .btn-info:active, .btn-info.active { background-color: #2aabd2; border-color: #28a4c9; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #2aabd2; background-image: none; } .btn-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #e38d13; } .btn-warning:hover, .btn-warning:focus { background-color: #eb9316; background-position: 0 -15px; } .btn-warning:active, .btn-warning.active { background-color: #eb9316; border-color: #e38d13; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #eb9316; background-image: none; } .btn-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #b92c28; } .btn-danger:hover, .btn-danger:focus { background-color: #c12e2a; background-position: 0 -15px; } .btn-danger:active, .btn-danger.active { background-color: #c12e2a; border-color: #b92c28; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #c12e2a; background-image: none; } .thumbnail, .img-thumbnail { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background-color: #e8e8e8; background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { background-color: #2e6da4; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .navbar-default { background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); } .navbar-brand, .navbar-nav > li > a { text-shadow: 0 1px 0 rgba(255, 255, 255, .25); } .navbar-inverse { background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-radius: 4px; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); } .navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a { text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); } .navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } @media (max-width: 767px) { .navbar .navbar-nav .open .dropdown-menu > .active > a, .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { color: #fff; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } } .alert { text-shadow: 0 1px 0 rgba(255, 255, 255, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); } .alert-success { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); background-repeat: repeat-x; border-color: #b2dba1; } .alert-info { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); background-repeat: repeat-x; border-color: #9acfea; } .alert-warning { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); background-repeat: repeat-x; border-color: #f5e79e; } .alert-danger { background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); background-repeat: repeat-x; border-color: #dca7a7; } .progress { background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; } .progress-bar { background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); background-repeat: repeat-x; } .progress-bar-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); background-repeat: repeat-x; } .progress-bar-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); background-repeat: repeat-x; } .progress-bar-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); background-repeat: repeat-x; } .progress-bar-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); background-repeat: repeat-x; } .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .list-group { border-radius: 4px; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { text-shadow: 0 -1px 0 #286090; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); background-repeat: repeat-x; border-color: #2b669a; } .list-group-item.active .badge, .list-group-item.active:hover .badge, .list-group-item.active:focus .badge { text-shadow: none; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); box-shadow: 0 1px 2px rgba(0, 0, 0, .05); } .panel-default > .panel-heading { background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .panel-primary > .panel-heading { background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .panel-success > .panel-heading { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); background-repeat: repeat-x; } .panel-info > .panel-heading { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); background-repeat: repeat-x; } .panel-warning > .panel-heading { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); background-repeat: repeat-x; } .panel-danger > .panel-heading { background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); background-repeat: repeat-x; } .well { background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; border-color: #dcdcdc; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); } /*# sourceMappingURL=bootstrap-theme.css.map */ ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap/3.3.6/css/bootstrap.css ================================================ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { margin: .67em 0; font-size: 2em; } mark { color: #000; background: #ff0; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -.5em; } sub { bottom: -.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { height: 0; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { margin: 0; font: inherit; color: inherit; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } fieldset { padding: .35em .625em .75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-spacing: 0; border-collapse: collapse; } td, th { padding: 0; } /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { *, *:before, *:after { color: #000 !important; text-shadow: none !important; background: transparent !important; -webkit-box-shadow: none !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="#"]:after, a[href^="javascript:"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .navbar { display: none; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table td, .table th { background-color: #fff !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .glyphicon-asterisk:before { content: "\002a"; } .glyphicon-plus:before { content: "\002b"; } .glyphicon-euro:before, .glyphicon-eur:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .glyphicon-cd:before { content: "\e201"; } .glyphicon-save-file:before { content: "\e202"; } .glyphicon-open-file:before { content: "\e203"; } .glyphicon-level-up:before { content: "\e204"; } .glyphicon-copy:before { content: "\e205"; } .glyphicon-paste:before { content: "\e206"; } .glyphicon-alert:before { content: "\e209"; } .glyphicon-equalizer:before { content: "\e210"; } .glyphicon-king:before { content: "\e211"; } .glyphicon-queen:before { content: "\e212"; } .glyphicon-pawn:before { content: "\e213"; } .glyphicon-bishop:before { content: "\e214"; } .glyphicon-knight:before { content: "\e215"; } .glyphicon-baby-formula:before { content: "\e216"; } .glyphicon-tent:before { content: "\26fa"; } .glyphicon-blackboard:before { content: "\e218"; } .glyphicon-bed:before { content: "\e219"; } .glyphicon-apple:before { content: "\f8ff"; } .glyphicon-erase:before { content: "\e221"; } .glyphicon-hourglass:before { content: "\231b"; } .glyphicon-lamp:before { content: "\e223"; } .glyphicon-duplicate:before { content: "\e224"; } .glyphicon-piggy-bank:before { content: "\e225"; } .glyphicon-scissors:before { content: "\e226"; } .glyphicon-bitcoin:before { content: "\e227"; } .glyphicon-btc:before { content: "\e227"; } .glyphicon-xbt:before { content: "\e227"; } .glyphicon-yen:before { content: "\00a5"; } .glyphicon-jpy:before { content: "\00a5"; } .glyphicon-ruble:before { content: "\20bd"; } .glyphicon-rub:before { content: "\20bd"; } .glyphicon-scale:before { content: "\e230"; } .glyphicon-ice-lolly:before { content: "\e231"; } .glyphicon-ice-lolly-tasted:before { content: "\e232"; } .glyphicon-education:before { content: "\e233"; } .glyphicon-option-horizontal:before { content: "\e234"; } .glyphicon-option-vertical:before { content: "\e235"; } .glyphicon-menu-hamburger:before { content: "\e236"; } .glyphicon-modal-window:before { content: "\e237"; } .glyphicon-oil:before { content: "\e238"; } .glyphicon-grain:before { content: "\e239"; } .glyphicon-sunglasses:before { content: "\e240"; } .glyphicon-text-size:before { content: "\e241"; } .glyphicon-text-color:before { content: "\e242"; } .glyphicon-text-background:before { content: "\e243"; } .glyphicon-object-align-top:before { content: "\e244"; } .glyphicon-object-align-bottom:before { content: "\e245"; } .glyphicon-object-align-horizontal:before { content: "\e246"; } .glyphicon-object-align-left:before { content: "\e247"; } .glyphicon-object-align-vertical:before { content: "\e248"; } .glyphicon-object-align-right:before { content: "\e249"; } .glyphicon-triangle-right:before { content: "\e250"; } .glyphicon-triangle-left:before { content: "\e251"; } .glyphicon-triangle-bottom:before { content: "\e252"; } .glyphicon-triangle-top:before { content: "\e253"; } .glyphicon-console:before { content: "\e254"; } .glyphicon-superscript:before { content: "\e255"; } .glyphicon-subscript:before { content: "\e256"; } .glyphicon-menu-left:before { content: "\e257"; } .glyphicon-menu-right:before { content: "\e258"; } .glyphicon-menu-down:before { content: "\e259"; } .glyphicon-menu-up:before { content: "\e260"; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #333; background-color: #fff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #337ab7; text-decoration: none; } a:hover, a:focus { color: #23527c; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; max-width: 100%; height: auto; } .img-rounded { border-radius: 6px; } .img-thumbnail { display: inline-block; max-width: 100%; height: auto; padding: 4px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } [role="button"] { cursor: pointer; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 20px; margin-bottom: 10px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 10px; margin-bottom: 10px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small, .small { font-size: 85%; } mark, .mark { padding: .2em; background-color: #fcf8e3; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777; } .text-primary { color: #337ab7; } a.text-primary:hover, a.text-primary:focus { color: #286090; } .text-success { color: #3c763d; } a.text-success:hover, a.text-success:focus { color: #2b542c; } .text-info { color: #31708f; } a.text-info:hover, a.text-info:focus { color: #245269; } .text-warning { color: #8a6d3b; } a.text-warning:hover, a.text-warning:focus { color: #66512c; } .text-danger { color: #a94442; } a.text-danger:hover, a.text-danger:focus { color: #843534; } .bg-primary { color: #fff; background-color: #337ab7; } a.bg-primary:hover, a.bg-primary:focus { background-color: #286090; } .bg-success { background-color: #dff0d8; } a.bg-success:hover, a.bg-success:focus { background-color: #c1e2b3; } .bg-info { background-color: #d9edf7; } a.bg-info:hover, a.bg-info:focus { background-color: #afd9ee; } .bg-warning { background-color: #fcf8e3; } a.bg-warning:hover, a.bg-warning:focus { background-color: #f7ecb5; } .bg-danger { background-color: #f2dede; } a.bg-danger:hover, a.bg-danger:focus { background-color: #e4b9b9; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; margin-left: -5px; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } dl { margin-top: 0; margin-bottom: 20px; } dt, dd { line-height: 1.42857143; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; font-size: 17.5px; border-left: 5px solid #eee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; text-align: right; border-right: 5px solid #eee; border-left: 0; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } address { margin-bottom: 20px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } kbd { padding: 2px 4px; font-size: 90%; color: #fff; background-color: #333; border-radius: 3px; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; -webkit-box-shadow: none; box-shadow: none; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; color: #333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } @media (min-width: 768px) { .container { width: 750px; } } @media (min-width: 992px) { .container { width: 970px; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .row { margin-right: -15px; margin-left: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-right: 15px; padding-left: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0; } } table { background-color: transparent; } caption { padding-top: 8px; padding-bottom: 8px; color: #777; text-align: left; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 20px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 8px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #ddd; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #ddd; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #ddd; } .table .table { background-color: #fff; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #ddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #ddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-of-type(odd) { background-color: #f9f9f9; } .table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { position: static; display: table-column; float: none; } table td[class*="col-"], table th[class*="col-"] { position: static; display: table-cell; float: none; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #f5f5f5; } .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #e8e8e8; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #dff0d8; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #d0e9c6; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #fcf8e3; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #faf2cc; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f2dede; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #ebcccc; } .table-responsive { min-height: .01%; overflow-x: auto; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #ddd; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.42857143; color: #555; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); } .form-control::-moz-placeholder { color: #999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999; } .form-control::-webkit-input-placeholder { color: #999; } .form-control::-ms-expand { background-color: transparent; border: 0; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: #eee; opacity: 1; } .form-control[disabled], fieldset[disabled] .form-control { cursor: not-allowed; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { line-height: 34px; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { line-height: 30px; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { line-height: 46px; } } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { min-height: 20px; padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-top: 4px \9; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { position: relative; display: inline-block; padding-left: 20px; margin-bottom: 0; font-weight: normal; vertical-align: middle; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { min-height: 34px; padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; } .form-control-static.input-lg, .form-control-static.input-sm { padding-right: 0; padding-left: 0; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm, select[multiple].input-sm { height: auto; } .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .form-group-sm select.form-control { height: 30px; line-height: 30px; } .form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control { height: auto; } .form-group-sm .form-control-static { height: 30px; min-height: 32px; padding: 6px 10px; font-size: 12px; line-height: 1.5; } .input-lg { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } select.input-lg { height: 46px; line-height: 46px; } textarea.input-lg, select[multiple].input-lg { height: auto; } .form-group-lg .form-control { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } .form-group-lg select.form-control { height: 46px; line-height: 46px; } .form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control { height: auto; } .form-group-lg .form-control-static { height: 46px; min-height: 38px; padding: 11px 16px; font-size: 18px; line-height: 1.3333333; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 42.5px; } .form-control-feedback { position: absolute; top: 0; right: 0; z-index: 2; display: block; width: 34px; height: 34px; line-height: 34px; text-align: center; pointer-events: none; } .input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback { width: 46px; height: 46px; line-height: 46px; } .input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { color: #3c763d; } .has-success .form-control { border-color: #3c763d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-success .form-control:focus { border-color: #2b542c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; } .has-success .input-group-addon { color: #3c763d; background-color: #dff0d8; border-color: #3c763d; } .has-success .form-control-feedback { color: #3c763d; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { color: #8a6d3b; } .has-warning .form-control { border-color: #8a6d3b; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-warning .form-control:focus { border-color: #66512c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; } .has-warning .input-group-addon { color: #8a6d3b; background-color: #fcf8e3; border-color: #8a6d3b; } .has-warning .form-control-feedback { color: #8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { color: #a94442; } .has-error .form-control { border-color: #a94442; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-error .form-control:focus { border-color: #843534; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; } .has-error .input-group-addon { color: #a94442; background-color: #f2dede; border-color: #a94442; } .has-error .form-control-feedback { color: #a94442; } .has-feedback label ~ .form-control-feedback { top: 25px; } .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .form-control-static { display: inline-block; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 7px; margin-top: 0; margin-bottom: 0; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 27px; } .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { padding-top: 7px; margin-bottom: 0; text-align: right; } } .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 11px; font-size: 18px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; font-size: 12px; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: normal; line-height: 1.42857143; text-align: center; white-space: nowrap; vertical-align: middle; -ms-touch-action: manipulation; touch-action: manipulation; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-image: none; border: 1px solid transparent; border-radius: 4px; } .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus, .btn.focus { color: #333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { cursor: not-allowed; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; opacity: .65; } a.btn.disabled, fieldset[disabled] a.btn { pointer-events: none; } .btn-default { color: #333; background-color: #fff; border-color: #ccc; } .btn-default:focus, .btn-default.focus { color: #333; background-color: #e6e6e6; border-color: #8c8c8c; } .btn-default:hover { color: #333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { color: #333; background-color: #d4d4d4; border-color: #8c8c8c; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus { background-color: #fff; border-color: #ccc; } .btn-default .badge { color: #fff; background-color: #333; } .btn-primary { color: #fff; background-color: #337ab7; border-color: #2e6da4; } .btn-primary:focus, .btn-primary.focus { color: #fff; background-color: #286090; border-color: #122b40; } .btn-primary:hover { color: #fff; background-color: #286090; border-color: #204d74; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #fff; background-color: #286090; border-color: #204d74; } .btn-primary:active:hover, .btn-primary.active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:active:focus, .btn-primary.active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:active.focus, .btn-primary.active.focus, .open > .dropdown-toggle.btn-primary.focus { color: #fff; background-color: #204d74; border-color: #122b40; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus { background-color: #337ab7; border-color: #2e6da4; } .btn-primary .badge { color: #337ab7; background-color: #fff; } .btn-success { color: #fff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:focus, .btn-success.focus { color: #fff; background-color: #449d44; border-color: #255625; } .btn-success:hover { color: #fff; background-color: #449d44; border-color: #398439; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #fff; background-color: #449d44; border-color: #398439; } .btn-success:active:hover, .btn-success.active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:active:focus, .btn-success.active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:active.focus, .btn-success.active.focus, .open > .dropdown-toggle.btn-success.focus { color: #fff; background-color: #398439; border-color: #255625; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus { background-color: #5cb85c; border-color: #4cae4c; } .btn-success .badge { color: #5cb85c; background-color: #fff; } .btn-info { color: #fff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:focus, .btn-info.focus { color: #fff; background-color: #31b0d5; border-color: #1b6d85; } .btn-info:hover { color: #fff; background-color: #31b0d5; border-color: #269abc; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #fff; background-color: #31b0d5; border-color: #269abc; } .btn-info:active:hover, .btn-info.active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:active:focus, .btn-info.active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { color: #fff; background-color: #269abc; border-color: #1b6d85; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus { background-color: #5bc0de; border-color: #46b8da; } .btn-info .badge { color: #5bc0de; background-color: #fff; } .btn-warning { color: #fff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:focus, .btn-warning.focus { color: #fff; background-color: #ec971f; border-color: #985f0d; } .btn-warning:hover { color: #fff; background-color: #ec971f; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #fff; background-color: #ec971f; border-color: #d58512; } .btn-warning:active:hover, .btn-warning.active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:active:focus, .btn-warning.active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:active.focus, .btn-warning.active.focus, .open > .dropdown-toggle.btn-warning.focus { color: #fff; background-color: #d58512; border-color: #985f0d; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus { background-color: #f0ad4e; border-color: #eea236; } .btn-warning .badge { color: #f0ad4e; background-color: #fff; } .btn-danger { color: #fff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:focus, .btn-danger.focus { color: #fff; background-color: #c9302c; border-color: #761c19; } .btn-danger:hover { color: #fff; background-color: #c9302c; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #fff; background-color: #c9302c; border-color: #ac2925; } .btn-danger:active:hover, .btn-danger.active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:active:focus, .btn-danger.active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:active.focus, .btn-danger.active.focus, .open > .dropdown-toggle.btn-danger.focus { color: #fff; background-color: #ac2925; border-color: #761c19; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus { background-color: #d9534f; border-color: #d43f3a; } .btn-danger .badge { color: #d9534f; background-color: #fff; } .btn-link { font-weight: normal; color: #337ab7; border-radius: 0; } .btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #23527c; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #777; text-decoration: none; } .btn-lg, .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } .btn-sm, .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs, .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity .15s linear; -o-transition: opacity .15s linear; transition: opacity .15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } tr.collapse.in { display: table-row; } tbody.collapse.in { display: table-row-group; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition-timing-function: ease; -o-transition-timing-function: ease; transition-timing-function: ease; -webkit-transition-duration: .35s; -o-transition-duration: .35s; transition-duration: .35s; -webkit-transition-property: height, visibility; -o-transition-property: height, visibility; transition-property: height, visibility; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px dashed; border-top: 4px solid \9; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropup, .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; text-align: left; list-style: none; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); box-shadow: 0 6px 12px rgba(0, 0, 0, .175); } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.42857143; color: #333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #262626; text-decoration: none; background-color: #f5f5f5; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #fff; text-decoration: none; background-color: #337ab7; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #777; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: not-allowed; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-menu-right { right: 0; left: auto; } .dropdown-menu-left { right: auto; left: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.42857143; color: #777; white-space: nowrap; } .dropdown-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 990; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { content: ""; border-top: 0; border-bottom: 4px dashed; border-bottom: 4px solid \9; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 2px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { right: 0; left: auto; } .navbar-right .dropdown-menu-left { right: auto; left: 0; } } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar { margin-left: -5px; } .btn-toolbar .btn, .btn-toolbar .btn-group, .btn-toolbar .input-group { float: left; } .btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .btn + .dropdown-toggle { padding-right: 8px; padding-left: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-left-radius: 0; border-top-right-radius: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-top-right-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn input[type="radio"], [data-toggle="buttons"] > .btn-group > .btn input[type="radio"], [data-toggle="buttons"] > .btn input[type="checkbox"], [data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { position: relative; z-index: 2; float: left; width: 100%; margin-bottom: 0; } .input-group .form-control:focus { z-index: 3; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 46px; line-height: 46px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 14px; font-weight: normal; line-height: 1; color: #555; text-align: center; background-color: #eee; border: 1px solid #ccc; border-radius: 4px; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 3px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 18px; border-radius: 6px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; font-size: 0; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -1px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { z-index: 2; } .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { margin-right: -1px; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { z-index: 2; margin-left: -1px; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eee; } .nav > li.disabled > a { color: #777; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #777; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eee; border-color: #337ab7; } .nav .nav-divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #ddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.42857143; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eee #eee #ddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555; cursor: default; background-color: #fff; border: 1px solid #ddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #fff; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 4px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #fff; background-color: #337ab7; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #fff; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { padding-right: 15px; padding-left: 15px; overflow-x: visible; -webkit-overflow-scrolling: touch; border-top: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-right: 0; padding-left: 0; } } .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } @media (max-device-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; } } .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; height: 50px; padding: 15px 15px; font-size: 18px; line-height: 20px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } .navbar-brand > img { display: block; } @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-right: 15px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 4px; } .navbar-toggle:focus { outline: 0; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 7.5px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 20px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; width: auto; vertical-align: middle; } .navbar-form .form-control-static { display: inline-block; } .navbar-form .input-group { display: inline-table; vertical-align: middle; } .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { width: auto; } .navbar-form .input-group > .form-control { width: 100%; } .navbar-form .control-label { margin-bottom: 0; vertical-align: middle; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .navbar-form .radio label, .navbar-form .checkbox label { padding-left: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .navbar-form .has-feedback .form-control-feedback { top: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } .navbar-form .form-group:last-child { margin-bottom: 0; } } @media (min-width: 768px) { .navbar-form { width: auto; padding-top: 0; padding-bottom: 0; margin-right: 0; margin-left: 0; border: 0; -webkit-box-shadow: none; box-shadow: none; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { margin-bottom: 0; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 15px; margin-bottom: 15px; } @media (min-width: 768px) { .navbar-text { float: left; margin-right: 15px; margin-left: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: -15px; } .navbar-right ~ .navbar-right { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777; } .navbar-default .navbar-nav > li > a { color: #777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #ccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #ddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #ddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #888; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { color: #555; background-color: #e7e7e7; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #ccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777; } .navbar-default .navbar-link:hover { color: #333; } .navbar-default .btn-link { color: #777; } .navbar-default .btn-link:hover, .navbar-default .btn-link:focus { color: #333; } .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { color: #ccc; } .navbar-inverse { background-color: #222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #9d9d9d; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-text { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #fff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #fff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { color: #fff; background-color: #080808; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #fff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #9d9d9d; } .navbar-inverse .navbar-link:hover { color: #fff; } .navbar-inverse .btn-link { color: #9d9d9d; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { color: #fff; } .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { color: #444; } .breadcrumb { padding: 8px 15px; margin-bottom: 20px; list-style: none; background-color: #f5f5f5; border-radius: 4px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { padding: 0 5px; color: #ccc; content: "/\00a0"; } .breadcrumb > .active { color: #777; } .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.42857143; color: #337ab7; text-decoration: none; background-color: #fff; border: 1px solid #ddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-top-left-radius: 4px; border-bottom-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { z-index: 2; color: #23527c; background-color: #eee; border-color: #ddd; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 3; color: #fff; cursor: default; background-color: #337ab7; border-color: #337ab7; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #777; cursor: not-allowed; background-color: #fff; border-color: #ddd; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 18px; line-height: 1.3333333; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-top-left-radius: 6px; border-bottom-left-radius: 6px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; line-height: 1.5; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-top-left-radius: 3px; border-bottom-left-radius: 3px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .pager { padding-left: 0; margin: 20px 0; text-align: center; list-style: none; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #eee; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #777; cursor: not-allowed; background-color: #fff; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #fff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #337ab7; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #286090; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: middle; background-color: #777; border-radius: 10px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .btn-xs .badge, .btn-group-xs > .btn .badge { top: 0; padding: 1px 5px; } a.badge:hover, a.badge:focus { color: #fff; text-decoration: none; cursor: pointer; } .list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #337ab7; background-color: #fff; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding-top: 30px; padding-bottom: 30px; margin-bottom: 30px; color: inherit; background-color: #eee; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 21px; font-weight: 200; } .jumbotron > hr { border-top-color: #d5d5d5; } .container .jumbotron, .container-fluid .jumbotron { padding-right: 15px; padding-left: 15px; border-radius: 6px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron, .container-fluid .jumbotron { padding-right: 60px; padding-left: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: border .2s ease-in-out; -o-transition: border .2s ease-in-out; transition: border .2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-right: auto; margin-left: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #337ab7; } .thumbnail .caption { padding: 9px; color: #333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f5f5f5; border-radius: 4px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; line-height: 20px; color: #fff; text-align: center; background-color: #337ab7; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); -webkit-transition: width .6s ease; -o-transition: width .6s ease; transition: width .6s ease; } .progress-striped .progress-bar, .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -webkit-background-size: 40px 40px; background-size: 40px 40px; } .progress.active .progress-bar, .progress-bar.active { -webkit-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #5cb85c; } .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media, .media-body { overflow: hidden; zoom: 1; } .media-body { width: 10000px; } .media-object { display: block; } .media-object.img-thumbnail { max-width: none; } .media-right, .media > .pull-right { padding-left: 10px; } .media-left, .media > .pull-left { padding-right: 10px; } .media-left, .media-right, .media-body { display: table-cell; vertical-align: top; } .media-middle { vertical-align: middle; } .media-bottom { vertical-align: bottom; } .media-heading { margin-top: 0; margin-bottom: 5px; } .media-list { padding-left: 0; list-style: none; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #fff; border: 1px solid #ddd; } .list-group-item:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } a.list-group-item, button.list-group-item { color: #555; } a.list-group-item .list-group-item-heading, button.list-group-item .list-group-item-heading { color: #333; } a.list-group-item:hover, button.list-group-item:hover, a.list-group-item:focus, button.list-group-item:focus { color: #555; text-decoration: none; background-color: #f5f5f5; } button.list-group-item { width: 100%; text-align: left; } .list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { color: #777; cursor: not-allowed; background-color: #eee; } .list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { color: inherit; } .list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { color: #777; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #fff; background-color: #337ab7; border-color: #337ab7; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #c7ddef; } .list-group-item-success { color: #3c763d; background-color: #dff0d8; } a.list-group-item-success, button.list-group-item-success { color: #3c763d; } a.list-group-item-success .list-group-item-heading, button.list-group-item-success .list-group-item-heading { color: inherit; } a.list-group-item-success:hover, button.list-group-item-success:hover, a.list-group-item-success:focus, button.list-group-item-success:focus { color: #3c763d; background-color: #d0e9c6; } a.list-group-item-success.active, button.list-group-item-success.active, a.list-group-item-success.active:hover, button.list-group-item-success.active:hover, a.list-group-item-success.active:focus, button.list-group-item-success.active:focus { color: #fff; background-color: #3c763d; border-color: #3c763d; } .list-group-item-info { color: #31708f; background-color: #d9edf7; } a.list-group-item-info, button.list-group-item-info { color: #31708f; } a.list-group-item-info .list-group-item-heading, button.list-group-item-info .list-group-item-heading { color: inherit; } a.list-group-item-info:hover, button.list-group-item-info:hover, a.list-group-item-info:focus, button.list-group-item-info:focus { color: #31708f; background-color: #c4e3f3; } a.list-group-item-info.active, button.list-group-item-info.active, a.list-group-item-info.active:hover, button.list-group-item-info.active:hover, a.list-group-item-info.active:focus, button.list-group-item-info.active:focus { color: #fff; background-color: #31708f; border-color: #31708f; } .list-group-item-warning { color: #8a6d3b; background-color: #fcf8e3; } a.list-group-item-warning, button.list-group-item-warning { color: #8a6d3b; } a.list-group-item-warning .list-group-item-heading, button.list-group-item-warning .list-group-item-heading { color: inherit; } a.list-group-item-warning:hover, button.list-group-item-warning:hover, a.list-group-item-warning:focus, button.list-group-item-warning:focus { color: #8a6d3b; background-color: #faf2cc; } a.list-group-item-warning.active, button.list-group-item-warning.active, a.list-group-item-warning.active:hover, button.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus, button.list-group-item-warning.active:focus { color: #fff; background-color: #8a6d3b; border-color: #8a6d3b; } .list-group-item-danger { color: #a94442; background-color: #f2dede; } a.list-group-item-danger, button.list-group-item-danger { color: #a94442; } a.list-group-item-danger .list-group-item-heading, button.list-group-item-danger .list-group-item-heading { color: inherit; } a.list-group-item-danger:hover, button.list-group-item-danger:hover, a.list-group-item-danger:focus, button.list-group-item-danger:focus { color: #a94442; background-color: #ebcccc; } a.list-group-item-danger.active, button.list-group-item-danger.active, a.list-group-item-danger.active:hover, button.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus, button.list-group-item-danger.active:focus { color: #fff; background-color: #a94442; border-color: #a94442; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 20px; background-color: #fff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); box-shadow: 0 1px 1px rgba(0, 0, 0, .05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; color: inherit; } .panel-title > a, .panel-title > small, .panel-title > .small, .panel-title > small > a, .panel-title > .small > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #ddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .list-group, .panel > .panel-collapse > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { border-top-left-radius: 0; border-top-right-radius: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { padding-right: 15px; padding-left: 15px; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 3px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 3px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { border-top: 1px solid #ddd; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { margin-bottom: 0; border: 0; } .panel-group { margin-bottom: 20px; } .panel-group .panel { margin-bottom: 0; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse > .panel-body, .panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #ddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #ddd; } .panel-default { border-color: #ddd; } .panel-default > .panel-heading { color: #333; background-color: #f5f5f5; border-color: #ddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ddd; } .panel-primary { border-color: #337ab7; } .panel-primary > .panel-heading { color: #fff; background-color: #337ab7; border-color: #337ab7; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #337ab7; } .panel-primary > .panel-heading .badge { color: #337ab7; background-color: #fff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #337ab7; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-heading .badge { color: #dff0d8; background-color: #3c763d; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #d6e9c6; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #faebcc; } .panel-warning > .panel-heading { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #faebcc; } .panel-warning > .panel-heading .badge { color: #fcf8e3; background-color: #8a6d3b; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #faebcc; } .panel-danger { border-color: #ebccd1; } .panel-danger > .panel-heading { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ebccd1; } .panel-danger > .panel-heading .badge { color: #f2dede; background-color: #a94442; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ebccd1; } .embed-responsive { position: relative; display: block; height: 0; padding: 0; overflow: hidden; } .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video { position: absolute; top: 0; bottom: 0; left: 0; width: 100%; height: 100%; border: 0; } .embed-responsive-16by9 { padding-bottom: 56.25%; } .embed-responsive-4by3 { padding-bottom: 75%; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, .15); } .well-lg { padding: 24px; border-radius: 6px; } .well-sm { padding: 9px; border-radius: 3px; } .close { float: right; font-size: 21px; font-weight: bold; line-height: 1; color: #000; text-shadow: 0 1px 0 #fff; filter: alpha(opacity=20); opacity: .2; } .close:hover, .close:focus { color: #000; text-decoration: none; cursor: pointer; filter: alpha(opacity=50); opacity: .5; } button.close { -webkit-appearance: none; padding: 0; cursor: pointer; background: transparent; border: 0; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; display: none; overflow: hidden; -webkit-overflow-scrolling: touch; outline: 0; } .modal.fade .modal-dialog { -webkit-transition: -webkit-transform .3s ease-out; -o-transition: -o-transform .3s ease-out; transition: transform .3s ease-out; -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); -o-transform: translate(0, -25%); transform: translate(0, -25%); } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); -o-transform: translate(0, 0); transform: translate(0, 0); } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #999; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; outline: 0; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); box-shadow: 0 3px 9px rgba(0, 0, 0, .5); } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; background-color: #000; } .modal-backdrop.fade { filter: alpha(opacity=0); opacity: 0; } .modal-backdrop.in { filter: alpha(opacity=50); opacity: .5; } .modal-header { padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.42857143; } .modal-body { position: relative; padding: 15px; } .modal-footer { padding: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); box-shadow: 0 5px 15px rgba(0, 0, 0, .5); } .modal-sm { width: 300px; } } @media (min-width: 992px) { .modal-lg { width: 900px; } } .tooltip { position: absolute; z-index: 1070; display: block; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; font-style: normal; font-weight: normal; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; word-wrap: normal; white-space: normal; filter: alpha(opacity=0); opacity: 0; line-break: auto; } .tooltip.in { filter: alpha(opacity=90); opacity: .9; } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #fff; text-align: center; background-color: #000; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-left .tooltip-arrow { right: 5px; bottom: 0; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-right .tooltip-arrow { bottom: 0; left: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-width: 5px 5px 5px 0; border-right-color: #000; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-width: 5px 0 5px 5px; border-left-color: #000; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-left .tooltip-arrow { top: 0; right: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-right .tooltip-arrow { top: 0; left: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .popover { position: absolute; top: 0; left: 0; z-index: 1060; display: none; max-width: 276px; padding: 1px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 1.42857143; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; word-wrap: normal; white-space: normal; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); box-shadow: 0 5px 10px rgba(0, 0, 0, .2); line-break: auto; } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } .popover-content { padding: 9px 14px; } .popover > .arrow, .popover > .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover > .arrow { border-width: 11px; } .popover > .arrow:after { content: ""; border-width: 10px; } .popover.top > .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999; border-top-color: rgba(0, 0, 0, .25); border-bottom-width: 0; } .popover.top > .arrow:after { bottom: 1px; margin-left: -10px; content: " "; border-top-color: #fff; border-bottom-width: 0; } .popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999; border-right-color: rgba(0, 0, 0, .25); border-left-width: 0; } .popover.right > .arrow:after { bottom: -10px; left: 1px; content: " "; border-right-color: #fff; border-left-width: 0; } .popover.bottom > .arrow { top: -11px; left: 50%; margin-left: -11px; border-top-width: 0; border-bottom-color: #999; border-bottom-color: rgba(0, 0, 0, .25); } .popover.bottom > .arrow:after { top: 1px; margin-left: -10px; content: " "; border-top-width: 0; border-bottom-color: #fff; } .popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; border-right-width: 0; border-left-color: #999; border-left-color: rgba(0, 0, 0, .25); } .popover.left > .arrow:after { right: 1px; bottom: -10px; content: " "; border-right-width: 0; border-left-color: #fff; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: .6s ease-in-out left; -o-transition: .6s ease-in-out left; transition: .6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } @media all and (transform-3d), (-webkit-transform-3d) { .carousel-inner > .item { -webkit-transition: -webkit-transform .6s ease-in-out; -o-transition: -o-transform .6s ease-in-out; transition: transform .6s ease-in-out; -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; perspective: 1000px; } .carousel-inner > .item.next, .carousel-inner > .item.active.right { left: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } .carousel-inner > .item.prev, .carousel-inner > .item.active.left { left: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { left: 0; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; font-size: 20px; color: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); background-color: rgba(0, 0, 0, 0); filter: alpha(opacity=50); opacity: .5; } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); background-repeat: repeat-x; } .carousel-control.right { right: 0; left: auto; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); background-repeat: repeat-x; } .carousel-control:hover, .carousel-control:focus { color: #fff; text-decoration: none; filter: alpha(opacity=90); outline: 0; opacity: .9; } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; margin-top: -10px; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; font-family: serif; line-height: 1; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; padding-left: 0; margin-left: -30%; text-align: center; list-style: none; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); border: 1px solid #fff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #fff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -10px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -10px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -10px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-header:before, .modal-header:after, .modal-footer:before, .modal-footer:after { display: table; content: " "; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-header:after, .modal-footer:after { clear: both; } .center-block { display: block; margin-right: auto; margin-left: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table !important; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table !important; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table !important; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table !important; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table !important; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } /*# sourceMappingURL=bootstrap.css.map */ ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap/3.3.6/js/bootstrap.js ================================================ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under the MIT license */ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } +function ($) { 'use strict'; var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.6 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.6 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.6' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.6 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.6' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state += 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked')) changed = false $parent.find('.active').removeClass('active') this.$element.addClass('active') } else if ($input.prop('type') == 'checkbox') { if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false this.$element.toggleClass('active') } $input.prop('checked', this.$element.hasClass('active')) if (changed) $input.trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) this.$element.toggleClass('active') } } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') Plugin.call($btn, 'toggle') if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.6 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = null this.sliding = null this.interval = null this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.6' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { if (/input|textarea/i.test(e.target.tagName)) return switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var activeIndex = this.getItemIndex(active) var willWrap = (direction == 'prev' && activeIndex === 0) || (direction == 'next' && activeIndex == (this.$items.length - 1)) if (willWrap && !this.options.wrap) return active var delta = direction == 'prev' ? -1 : 1 var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var that = this if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.6 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.6' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.6 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.6' function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) }) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $(document.createElement('div')) .addClass('dropdown-backdrop') .insertAfter($(this)) .on('click', clearMenus) } var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger($.Event('shown.bs.dropdown', relatedTarget)) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if (!isActive && e.which != 27 || isActive && e.which == 27) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('.dropdown-menu' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // up if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') } // DROPDOWN PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } // APPLY TO STANDARD DROPDOWN ELEMENTS // =================================== $(document) .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) }(jQuery); /* ======================================================================== * Bootstrap: modal.js v3.3.6 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // MODAL CLASS DEFINITION // ====================== var Modal = function (element, options) { this.options = options this.$body = $(document.body) this.$element = $(element) this.$dialog = this.$element.find('.modal-dialog') this.$backdrop = null this.isShown = null this.originalBodyPad = null this.scrollbarWidth = 0 this.ignoreBackdropClick = false if (this.options.remote) { this.$element .find('.modal-content') .load(this.options.remote, $.proxy(function () { this.$element.trigger('loaded.bs.modal') }, this)) } } Modal.VERSION = '3.3.6' Modal.TRANSITION_DURATION = 300 Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, keyboard: true, show: true } Modal.prototype.toggle = function (_relatedTarget) { return this.isShown ? this.hide() : this.show(_relatedTarget) } Modal.prototype.show = function (_relatedTarget) { var that = this var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) this.$element.trigger(e) if (this.isShown || e.isDefaultPrevented()) return this.isShown = true this.checkScrollbar() this.setScrollbar() this.$body.addClass('modal-open') this.escape() this.resize() this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$dialog.on('mousedown.dismiss.bs.modal', function () { that.$element.one('mouseup.dismiss.bs.modal', function (e) { if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true }) }) this.backdrop(function () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(that.$body) // don't move modals dom position } that.$element .show() .scrollTop(0) that.adjustDialog() if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') that.enforceFocus() var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) transition ? that.$dialog // wait for modal to slide in .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } Modal.prototype.hide = function (e) { if (e) e.preventDefault() e = $.Event('hide.bs.modal') this.$element.trigger(e) if (!this.isShown || e.isDefaultPrevented()) return this.isShown = false this.escape() this.resize() $(document).off('focusin.bs.modal') this.$element .removeClass('in') .off('click.dismiss.bs.modal') .off('mouseup.dismiss.bs.modal') this.$dialog.off('mousedown.dismiss.bs.modal') $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } Modal.prototype.enforceFocus = function () { $(document) .off('focusin.bs.modal') // guard against infinite focus loop .on('focusin.bs.modal', $.proxy(function (e) { if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { this.$element.trigger('focus') } }, this)) } Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { this.$element.off('keydown.dismiss.bs.modal') } } Modal.prototype.resize = function () { if (this.isShown) { $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) } else { $(window).off('resize.bs.modal') } } Modal.prototype.hideModal = function () { var that = this this.$element.hide() this.backdrop(function () { that.$body.removeClass('modal-open') that.resetAdjustments() that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } Modal.prototype.removeBackdrop = function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null } Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate this.$backdrop = $(document.createElement('div')) .addClass('modal-backdrop ' + animate) .appendTo(this.$body) this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { if (this.ignoreBackdropClick) { this.ignoreBackdropClick = false return } if (e.target !== e.currentTarget) return this.options.backdrop == 'static' ? this.$element[0].focus() : this.hide() }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow this.$backdrop.addClass('in') if (!callback) return doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { this.$backdrop.removeClass('in') var callbackRemove = function () { that.removeBackdrop() callback && callback() } $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { callback() } } // these following methods are used to handle overflowing modals Modal.prototype.handleUpdate = function () { this.adjustDialog() } Modal.prototype.adjustDialog = function () { var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight this.$element.css({ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' }) } Modal.prototype.resetAdjustments = function () { this.$element.css({ paddingLeft: '', paddingRight: '' }) } Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 var documentElementRect = document.documentElement.getBoundingClientRect() fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) this.originalBodyPad = document.body.style.paddingRight || '' if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } Modal.prototype.resetScrollbar = function () { this.$body.css('padding-right', this.originalBodyPad) } Modal.prototype.measureScrollbar = function () { // thx walsh var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth this.$body[0].removeChild(scrollDiv) return scrollbarWidth } // MODAL PLUGIN DEFINITION // ======================= function Plugin(option, _relatedTarget) { return this.each(function () { var $this = $(this) var data = $this.data('bs.modal') var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('bs.modal', (data = new Modal(this, options))) if (typeof option == 'string') data[option](_relatedTarget) else if (options.show) data.show(_relatedTarget) }) } var old = $.fn.modal $.fn.modal = Plugin $.fn.modal.Constructor = Modal // MODAL NO CONFLICT // ================= $.fn.modal.noConflict = function () { $.fn.modal = old return this } // MODAL DATA-API // ============== $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { var $this = $(this) var href = $this.attr('href') var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) if ($this.is('a')) e.preventDefault() $target.one('show.bs.modal', function (showEvent) { if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown $target.one('hidden.bs.modal', function () { $this.is(':visible') && $this.trigger('focus') }) }) Plugin.call($target, option, this) }) }(jQuery); /* ======================================================================== * Bootstrap: tooltip.js v3.3.6 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TOOLTIP PUBLIC CLASS DEFINITION // =============================== var Tooltip = function (element, options) { this.type = null this.options = null this.enabled = null this.timeout = null this.hoverState = null this.$element = null this.inState = null this.init('tooltip', element, options) } Tooltip.VERSION = '3.3.6' Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, placement: 'top', selector: false, template: '', trigger: 'hover focus', title: '', delay: 0, html: false, container: false, viewport: { selector: 'body', padding: 0 } } Tooltip.prototype.init = function (type, element, options) { this.enabled = true this.type = type this.$element = $(element) this.options = this.getOptions(options) this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) this.inState = { click: false, hover: false, focus: false } if (this.$element[0] instanceof document.constructor && !this.options.selector) { throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') } var triggers = this.options.trigger.split(' ') for (var i = triggers.length; i--;) { var trigger = triggers[i] if (trigger == 'click') { this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) } else if (trigger != 'manual') { var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) } } this.options.selector ? (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : this.fixTitle() } Tooltip.prototype.getDefaults = function () { return Tooltip.DEFAULTS } Tooltip.prototype.getOptions = function (options) { options = $.extend({}, this.getDefaults(), this.$element.data(), options) if (options.delay && typeof options.delay == 'number') { options.delay = { show: options.delay, hide: options.delay } } return options } Tooltip.prototype.getDelegateOptions = function () { var options = {} var defaults = this.getDefaults() this._options && $.each(this._options, function (key, value) { if (defaults[key] != value) options[key] = value }) return options } Tooltip.prototype.enter = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true } if (self.tip().hasClass('in') || self.hoverState == 'in') { self.hoverState = 'in' return } clearTimeout(self.timeout) self.hoverState = 'in' if (!self.options.delay || !self.options.delay.show) return self.show() self.timeout = setTimeout(function () { if (self.hoverState == 'in') self.show() }, self.options.delay.show) } Tooltip.prototype.isInStateTrue = function () { for (var key in this.inState) { if (this.inState[key]) return true } return false } Tooltip.prototype.leave = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false } if (self.isInStateTrue()) return clearTimeout(self.timeout) self.hoverState = 'out' if (!self.options.delay || !self.options.delay.hide) return self.hide() self.timeout = setTimeout(function () { if (self.hoverState == 'out') self.hide() }, self.options.delay.hide) } Tooltip.prototype.show = function () { var e = $.Event('show.bs.' + this.type) if (this.hasContent() && this.enabled) { this.$element.trigger(e) var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this var $tip = this.tip() var tipId = this.getUID(this.type) this.setContent() $tip.attr('id', tipId) this.$element.attr('aria-describedby', tipId) if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? this.options.placement.call(this, $tip[0], this.$element[0]) : this.options.placement var autoToken = /\s?auto?\s?/i var autoPlace = autoToken.test(placement) if (autoPlace) placement = placement.replace(autoToken, '') || 'top' $tip .detach() .css({ top: 0, left: 0, display: 'block' }) .addClass(placement) .data('bs.' + this.type, this) this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) this.$element.trigger('inserted.bs.' + this.type) var pos = this.getPosition() var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (autoPlace) { var orgPlacement = placement var viewportDim = this.getPosition(this.$viewport) placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : placement $tip .removeClass(orgPlacement) .addClass(placement) } var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) this.applyPlacement(calculatedOffset, placement) var complete = function () { var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } Tooltip.prototype.applyPlacement = function (offset, placement) { var $tip = this.tip() var width = $tip[0].offsetWidth var height = $tip[0].offsetHeight // manually read margins because getBoundingClientRect includes difference var marginTop = parseInt($tip.css('margin-top'), 10) var marginLeft = parseInt($tip.css('margin-left'), 10) // we must check for NaN for ie 8/9 if (isNaN(marginTop)) marginTop = 0 if (isNaN(marginLeft)) marginLeft = 0 offset.top += marginTop offset.left += marginLeft // $.fn.offset doesn't round pixel values // so we use setOffset directly with our own function B-0 $.offset.setOffset($tip[0], $.extend({ using: function (props) { $tip.css({ top: Math.round(props.top), left: Math.round(props.left) }) } }, offset), 0) $tip.addClass('in') // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (placement == 'top' && actualHeight != height) { offset.top = offset.top + height - actualHeight } var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) if (delta.left) offset.left += delta.left else offset.top += delta.top var isVertical = /top|bottom/.test(placement) var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { this.arrow() .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') .css(isVertical ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) $tip.removeClass('fade in top bottom left right') } Tooltip.prototype.hide = function (callback) { var that = this var $tip = $(this.$tip) var e = $.Event('hide.bs.' + this.type) function complete() { if (that.hoverState != 'in') $tip.detach() that.$element .removeAttr('aria-describedby') .trigger('hidden.bs.' + that.type) callback && callback() } this.$element.trigger(e) if (e.isDefaultPrevented()) return $tip.removeClass('in') $.support.transition && $tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null return this } Tooltip.prototype.fixTitle = function () { var $e = this.$element if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') } } Tooltip.prototype.hasContent = function () { return this.getTitle() } Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element var el = $element[0] var isBody = el.tagName == 'BODY' var elRect = el.getBoundingClientRect() if (elRect.width == null) { // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) } var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } } Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { var delta = { top: 0, left: 0 } if (!this.$viewport) return delta var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 var viewportDimensions = this.getPosition(this.$viewport) if (/right|left/.test(placement)) { var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight if (topEdgeOffset < viewportDimensions.top) { // top overflow delta.top = viewportDimensions.top - topEdgeOffset } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset } } else { var leftEdgeOffset = pos.left - viewportPadding var rightEdgeOffset = pos.left + viewportPadding + actualWidth if (leftEdgeOffset < viewportDimensions.left) { // left overflow delta.left = viewportDimensions.left - leftEdgeOffset } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset } } return delta } Tooltip.prototype.getTitle = function () { var title var $e = this.$element var o = this.options title = $e.attr('data-original-title') || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) return title } Tooltip.prototype.getUID = function (prefix) { do prefix += ~~(Math.random() * 1000000) while (document.getElementById(prefix)) return prefix } Tooltip.prototype.tip = function () { if (!this.$tip) { this.$tip = $(this.options.template) if (this.$tip.length != 1) { throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') } } return this.$tip } Tooltip.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } Tooltip.prototype.enable = function () { this.enabled = true } Tooltip.prototype.disable = function () { this.enabled = false } Tooltip.prototype.toggleEnabled = function () { this.enabled = !this.enabled } Tooltip.prototype.toggle = function (e) { var self = this if (e) { self = $(e.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(e.currentTarget, this.getDelegateOptions()) $(e.currentTarget).data('bs.' + this.type, self) } } if (e) { self.inState.click = !self.inState.click if (self.isInStateTrue()) self.enter(self) else self.leave(self) } else { self.tip().hasClass('in') ? self.leave(self) : self.enter(self) } } Tooltip.prototype.destroy = function () { var that = this clearTimeout(this.timeout) this.hide(function () { that.$element.off('.' + that.type).removeData('bs.' + that.type) if (that.$tip) { that.$tip.detach() } that.$tip = null that.$arrow = null that.$viewport = null }) } // TOOLTIP PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tooltip') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tooltip $.fn.tooltip = Plugin $.fn.tooltip.Constructor = Tooltip // TOOLTIP NO CONFLICT // =================== $.fn.tooltip.noConflict = function () { $.fn.tooltip = old return this } }(jQuery); /* ======================================================================== * Bootstrap: popover.js v3.3.6 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // POPOVER PUBLIC CLASS DEFINITION // =============================== var Popover = function (element, options) { this.init('popover', element, options) } if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') Popover.VERSION = '3.3.6' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', trigger: 'click', content: '', template: '' }) // NOTE: POPOVER EXTENDS tooltip.js // ================================ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) Popover.prototype.constructor = Popover Popover.prototype.getDefaults = function () { return Popover.DEFAULTS } Popover.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) $tip.removeClass('fade top bottom left right in') // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do // this manually by checking the contents. if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() } Popover.prototype.hasContent = function () { return this.getTitle() || this.getContent() } Popover.prototype.getContent = function () { var $e = this.$element var o = this.options return $e.attr('data-content') || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) } Popover.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.arrow')) } // POPOVER PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.popover') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.popover', (data = new Popover(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.popover $.fn.popover = Plugin $.fn.popover.Constructor = Popover // POPOVER NO CONFLICT // =================== $.fn.popover.noConflict = function () { $.fn.popover = old return this } }(jQuery); /* ======================================================================== * Bootstrap: scrollspy.js v3.3.6 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // SCROLLSPY CLASS DEFINITION // ========================== function ScrollSpy(element, options) { this.$body = $(document.body) this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) this.options = $.extend({}, ScrollSpy.DEFAULTS, options) this.selector = (this.options.target || '') + ' .nav li > a' this.offsets = [] this.targets = [] this.activeTarget = null this.scrollHeight = 0 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) this.refresh() this.process() } ScrollSpy.VERSION = '3.3.6' ScrollSpy.DEFAULTS = { offset: 10 } ScrollSpy.prototype.getScrollHeight = function () { return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = function () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets = [] this.targets = [] this.scrollHeight = this.getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = this.$scrollElement.scrollTop() } this.$body .find(this.selector) .map(function () { var $el = $(this) var href = $el.data('target') || $el.attr('href') var $href = /^#./.test(href) && $(href) return ($href && $href.length && $href.is(':visible') && [[$href[offsetMethod]().top + offsetBase, href]]) || null }) .sort(function (a, b) { return a[0] - b[0] }) .each(function () { that.offsets.push(this[0]) that.targets.push(this[1]) }) } ScrollSpy.prototype.process = function () { var scrollTop = this.$scrollElement.scrollTop() + this.options.offset var scrollHeight = this.getScrollHeight() var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() var offsets = this.offsets var targets = this.targets var activeTarget = this.activeTarget var i if (this.scrollHeight != scrollHeight) { this.refresh() } if (scrollTop >= maxScroll) { return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return this.clear() } for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) && this.activate(targets[i]) } } ScrollSpy.prototype.activate = function (target) { this.activeTarget = target this.clear() var selector = this.selector + '[data-target="' + target + '"],' + this.selector + '[href="' + target + '"]' var active = $(selector) .parents('li') .addClass('active') if (active.parent('.dropdown-menu').length) { active = active .closest('li.dropdown') .addClass('active') } active.trigger('activate.bs.scrollspy') } ScrollSpy.prototype.clear = function () { $(this.selector) .parentsUntil(this.options.target, '.active') .removeClass('active') } // SCROLLSPY PLUGIN DEFINITION // =========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.scrollspy') var options = typeof option == 'object' && option if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy $.fn.scrollspy = Plugin $.fn.scrollspy.Constructor = ScrollSpy // SCROLLSPY NO CONFLICT // ===================== $.fn.scrollspy.noConflict = function () { $.fn.scrollspy = old return this } // SCROLLSPY DATA-API // ================== $(window).on('load.bs.scrollspy.data-api', function () { $('[data-spy="scroll"]').each(function () { var $spy = $(this) Plugin.call($spy, $spy.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: tab.js v3.3.6 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TAB CLASS DEFINITION // ==================== var Tab = function (element) { // jscs:disable requireDollarBeforejQueryAssignment this.element = $(element) // jscs:enable requireDollarBeforejQueryAssignment } Tab.VERSION = '3.3.6' Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element var $ul = $this.closest('ul:not(.dropdown-menu)') var selector = $this.data('target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } if ($this.parent('li').hasClass('active')) return var $previous = $ul.find('.active:last a') var hideEvent = $.Event('hide.bs.tab', { relatedTarget: $this[0] }) var showEvent = $.Event('show.bs.tab', { relatedTarget: $previous[0] }) $previous.trigger(hideEvent) $this.trigger(showEvent) if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { $previous.trigger({ type: 'hidden.bs.tab', relatedTarget: $this[0] }) $this.trigger({ type: 'shown.bs.tab', relatedTarget: $previous[0] }) }) } Tab.prototype.activate = function (element, container, callback) { var $active = container.find('> .active') var transition = callback && $.support.transition && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') .removeClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', false) element .addClass('active') .find('[data-toggle="tab"]') .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition element.addClass('in') } else { element.removeClass('fade') } if (element.parent('.dropdown-menu').length) { element .closest('li.dropdown') .addClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', true) } callback && callback() } $active.length && transition ? $active .one('bsTransitionEnd', next) .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') } // TAB PLUGIN DEFINITION // ===================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tab') if (!data) $this.data('bs.tab', (data = new Tab(this))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tab $.fn.tab = Plugin $.fn.tab.Constructor = Tab // TAB NO CONFLICT // =============== $.fn.tab.noConflict = function () { $.fn.tab = old return this } // TAB DATA-API // ============ var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') } $(document) .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); /* ======================================================================== * Bootstrap: affix.js v3.3.6 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // AFFIX CLASS DEFINITION // ====================== var Affix = function (element, options) { this.options = $.extend({}, Affix.DEFAULTS, options) this.$target = $(this.options.target) .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) this.$element = $(element) this.affixed = null this.unpin = null this.pinnedOffset = null this.checkPosition() } Affix.VERSION = '3.3.6' Affix.RESET = 'affix affix-top affix-bottom' Affix.DEFAULTS = { offset: 0, target: window } Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { var scrollTop = this.$target.scrollTop() var position = this.$element.offset() var targetHeight = this.$target.height() if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false if (this.affixed == 'bottom') { if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' } var initializing = this.affixed == null var colliderTop = initializing ? scrollTop : position.top var colliderHeight = initializing ? targetHeight : height if (offsetTop != null && scrollTop <= offsetTop) return 'top' if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' return false } Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') var scrollTop = this.$target.scrollTop() var position = this.$element.offset() return (this.pinnedOffset = position.top - scrollTop) } Affix.prototype.checkPositionWithEventLoop = function () { setTimeout($.proxy(this.checkPosition, this), 1) } Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom var scrollHeight = Math.max($(document).height(), $(document.body).height()) if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) if (this.affixed != affix) { if (this.unpin != null) this.$element.css('top', '') var affixType = 'affix' + (affix ? '-' + affix : '') var e = $.Event(affixType + '.bs.affix') this.$element.trigger(e) if (e.isDefaultPrevented()) return this.affixed = affix this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null this.$element .removeClass(Affix.RESET) .addClass(affixType) .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') } if (affix == 'bottom') { this.$element.offset({ top: scrollHeight - height - offsetBottom }) } } // AFFIX PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.affix') var options = typeof option == 'object' && option if (!data) $this.data('bs.affix', (data = new Affix(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.affix $.fn.affix = Plugin $.fn.affix.Constructor = Affix // AFFIX NO CONFLICT // ================= $.fn.affix.noConflict = function () { $.fn.affix = old return this } // AFFIX DATA-API // ============== $(window).on('load', function () { $('[data-spy="affix"]').each(function () { var $spy = $(this) var data = $spy.data() data.offset = data.offset || {} if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) }) }(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap/3.3.6/js/npm.js ================================================ // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. require('../../js/transition.js') require('../../js/alert.js') require('../../js/button.js') require('../../js/carousel.js') require('../../js/collapse.js') require('../../js/dropdown.js') require('../../js/modal.js') require('../../js/tooltip.js') require('../../js/popover.js') require('../../js/scrollspy.js') require('../../js/tab.js') require('../../js/affix.js') ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/css/bootstrap-select.css ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ .bootstrap-select { width: 220px \0; /*IE9 and below*/ } .bootstrap-select > .dropdown-toggle { width: 100%; padding-right: 25px; } .has-error .bootstrap-select .dropdown-toggle, .error .bootstrap-select .dropdown-toggle { border-color: #b94a48; } .bootstrap-select.fit-width { width: auto !important; } .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { width: 220px; } .bootstrap-select .dropdown-toggle:focus { outline: thin dotted #333333 !important; outline: 5px auto -webkit-focus-ring-color !important; outline-offset: -2px; } .bootstrap-select.form-control { margin-bottom: 0; padding: 0; border: none; } .bootstrap-select.form-control:not([class*="col-"]) { width: 100%; } .bootstrap-select.form-control.input-group-btn { z-index: auto; } .bootstrap-select.btn-group:not(.input-group-btn), .bootstrap-select.btn-group[class*="col-"] { float: none; display: inline-block; margin-left: 0; } .bootstrap-select.btn-group.dropdown-menu-right, .bootstrap-select.btn-group[class*="col-"].dropdown-menu-right, .row .bootstrap-select.btn-group[class*="col-"].dropdown-menu-right { float: right; } .form-inline .bootstrap-select.btn-group, .form-horizontal .bootstrap-select.btn-group, .form-group .bootstrap-select.btn-group { margin-bottom: 0; } .form-group-lg .bootstrap-select.btn-group.form-control, .form-group-sm .bootstrap-select.btn-group.form-control { padding: 0; } .form-inline .bootstrap-select.btn-group .form-control { width: 100%; } .bootstrap-select.btn-group.disabled, .bootstrap-select.btn-group > .disabled { cursor: not-allowed; } .bootstrap-select.btn-group.disabled:focus, .bootstrap-select.btn-group > .disabled:focus { outline: none !important; } .bootstrap-select.btn-group .dropdown-toggle .filter-option { display: inline-block; overflow: hidden; width: 100%; text-align: left; } .bootstrap-select.btn-group .dropdown-toggle .caret { position: absolute; top: 50%; right: 12px; margin-top: -2px; vertical-align: middle; } .bootstrap-select.btn-group[class*="col-"] .dropdown-toggle { width: 100%; } .bootstrap-select.btn-group .dropdown-menu { min-width: 100%; z-index: 1035; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bootstrap-select.btn-group .dropdown-menu.inner { position: static; float: none; border: 0; padding: 0; margin: 0; border-radius: 0; -webkit-box-shadow: none; box-shadow: none; } .bootstrap-select.btn-group .dropdown-menu li { position: relative; } .bootstrap-select.btn-group .dropdown-menu li.active small { color: #fff; } .bootstrap-select.btn-group .dropdown-menu li.disabled a { cursor: not-allowed; } .bootstrap-select.btn-group .dropdown-menu li a { cursor: pointer; } .bootstrap-select.btn-group .dropdown-menu li a.opt { position: relative; padding-left: 2.25em; } .bootstrap-select.btn-group .dropdown-menu li a span.check-mark { display: none; } .bootstrap-select.btn-group .dropdown-menu li a span.text { display: inline-block; } .bootstrap-select.btn-group .dropdown-menu li small { padding-left: 0.5em; } .bootstrap-select.btn-group .dropdown-menu .notify { position: absolute; bottom: 5px; width: 96%; margin: 0 2%; min-height: 26px; padding: 3px 5px; background: #f5f5f5; border: 1px solid #e3e3e3; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); pointer-events: none; opacity: 0.9; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bootstrap-select.btn-group .no-results { padding: 3px; background: #f5f5f5; margin: 0 5px; white-space: nowrap; } .bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option { position: static; } .bootstrap-select.btn-group.fit-width .dropdown-toggle .caret { position: static; top: auto; margin-top: -1px; } .bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark { position: absolute; display: inline-block; right: 15px; margin-top: 5px; } .bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text { margin-right: 34px; } .bootstrap-select.show-menu-arrow.open > .dropdown-toggle { z-index: 1036; } .bootstrap-select.show-menu-arrow .dropdown-toggle:before { content: ''; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 7px solid rgba(204, 204, 204, 0.2); position: absolute; bottom: -4px; left: 9px; display: none; } .bootstrap-select.show-menu-arrow .dropdown-toggle:after { content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 6px solid white; position: absolute; bottom: -4px; left: 10px; display: none; } .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before { bottom: auto; top: -3px; border-top: 7px solid rgba(204, 204, 204, 0.2); border-bottom: 0; } .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after { bottom: auto; top: -3px; border-top: 6px solid white; border-bottom: 0; } .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before { right: 12px; left: auto; } .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after { right: 13px; left: auto; } .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:before, .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:after { display: block; } .bs-searchbox, .bs-actionsbox, .bs-donebutton { padding: 4px 8px; } .bs-actionsbox { float: left; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bs-actionsbox .btn-group button { width: 50%; } .bs-donebutton { float: left; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bs-donebutton .btn-group button { width: 100%; } .bs-searchbox + .bs-actionsbox { padding: 0 8px 4px; } .bs-searchbox .form-control { margin-bottom: 0; width: 100%; } select.bs-select-hidden, select.selectpicker { display: none !important; } select.mobile-device { position: absolute !important; top: 0; left: 0; display: block !important; width: 100%; height: 100% !important; opacity: 0; } /*# sourceMappingURL=bootstrap-select.css.map */ ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/bootstrap-select.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { 'use strict'; // if (!String.prototype.includes) { (function () { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` var toString = {}.toString; var defineProperty = (function () { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch (error) { } return result; }()); var indexOf = ''.indexOf; var includes = function (search) { if (this == null) { throw TypeError(); } var string = String(this); if (search && toString.call(search) == '[object RegExp]') { throw TypeError(); } var stringLength = string.length; var searchString = String(search); var searchLength = searchString.length; var position = arguments.length > 1 ? arguments[1] : undefined; // `ToInteger` var pos = position ? Number(position) : 0; if (pos != pos) { // better `isNaN` pos = 0; } var start = Math.min(Math.max(pos, 0), stringLength); // Avoid the `indexOf` call if no match is possible if (searchLength + start > stringLength) { return false; } return indexOf.call(string, searchString, pos) != -1; }; if (defineProperty) { defineProperty(String.prototype, 'includes', { 'value': includes, 'configurable': true, 'writable': true }); } else { String.prototype.includes = includes; } }()); } if (!String.prototype.startsWith) { (function () { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` var defineProperty = (function () { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch (error) { } return result; }()); var toString = {}.toString; var startsWith = function (search) { if (this == null) { throw TypeError(); } var string = String(this); if (search && toString.call(search) == '[object RegExp]') { throw TypeError(); } var stringLength = string.length; var searchString = String(search); var searchLength = searchString.length; var position = arguments.length > 1 ? arguments[1] : undefined; // `ToInteger` var pos = position ? Number(position) : 0; if (pos != pos) { // better `isNaN` pos = 0; } var start = Math.min(Math.max(pos, 0), stringLength); // Avoid the `indexOf` call if no match is possible if (searchLength + start > stringLength) { return false; } var index = -1; while (++index < searchLength) { if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { return false; } } return true; }; if (defineProperty) { defineProperty(String.prototype, 'startsWith', { 'value': startsWith, 'configurable': true, 'writable': true }); } else { String.prototype.startsWith = startsWith; } }()); } if (!Object.keys) { Object.keys = function ( o, // object k, // key r // result array ){ // initialize object and result r=[]; // iterate over object keys for (k in o) // fill result array with non-prototypical keys r.hasOwnProperty.call(o, k) && r.push(k); // return result return r }; } // // Case insensitive contains search $.expr[':'].icontains = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.text()).toUpperCase(); return haystack.includes(meta[3].toUpperCase()); }; // Case insensitive begins search $.expr[':'].ibegins = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.text()).toUpperCase(); return haystack.startsWith(meta[3].toUpperCase()); }; // Case and accent insensitive contains search $.expr[':'].aicontains = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toUpperCase(); return haystack.includes(meta[3].toUpperCase()); }; // Case and accent insensitive begins search $.expr[':'].aibegins = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toUpperCase(); return haystack.startsWith(meta[3].toUpperCase()); }; /** * Remove all diatrics from the given text. * @access private * @param {String} text * @returns {String} */ function normalizeToBase(text) { var rExps = [ {re: /[\xC0-\xC6]/g, ch: "A"}, {re: /[\xE0-\xE6]/g, ch: "a"}, {re: /[\xC8-\xCB]/g, ch: "E"}, {re: /[\xE8-\xEB]/g, ch: "e"}, {re: /[\xCC-\xCF]/g, ch: "I"}, {re: /[\xEC-\xEF]/g, ch: "i"}, {re: /[\xD2-\xD6]/g, ch: "O"}, {re: /[\xF2-\xF6]/g, ch: "o"}, {re: /[\xD9-\xDC]/g, ch: "U"}, {re: /[\xF9-\xFC]/g, ch: "u"}, {re: /[\xC7-\xE7]/g, ch: "c"}, {re: /[\xD1]/g, ch: "N"}, {re: /[\xF1]/g, ch: "n"} ]; $.each(rExps, function () { text = text.replace(this.re, this.ch); }); return text; } function htmlEscape(html) { var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var source = '(?:' + Object.keys(escapeMap).join('|') + ')', testRegexp = new RegExp(source), replaceRegexp = new RegExp(source, 'g'), string = html == null ? '' : '' + html; return testRegexp.test(string) ? string.replace(replaceRegexp, function (match) { return escapeMap[match]; }) : string; } var Selectpicker = function (element, options, e) { if (e) { e.stopPropagation(); e.preventDefault(); } this.$element = $(element); this.$newElement = null; this.$button = null; this.$menu = null; this.$lis = null; this.options = options; // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a // data-attribute) if (this.options.title === null) { this.options.title = this.$element.attr('title'); } //Expose public methods this.val = Selectpicker.prototype.val; this.render = Selectpicker.prototype.render; this.refresh = Selectpicker.prototype.refresh; this.setStyle = Selectpicker.prototype.setStyle; this.selectAll = Selectpicker.prototype.selectAll; this.deselectAll = Selectpicker.prototype.deselectAll; this.destroy = Selectpicker.prototype.remove; this.remove = Selectpicker.prototype.remove; this.show = Selectpicker.prototype.show; this.hide = Selectpicker.prototype.hide; this.init(); }; Selectpicker.VERSION = '1.7.2'; // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. Selectpicker.DEFAULTS = { noneSelectedText: 'Nothing selected', noneResultsText: 'No results matched {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' ]; }, selectAllText: 'Select All', deselectAllText: 'Deselect All', doneButton: false, doneButtonText: 'Close', multipleSeparator: ', ', styleBase: 'btn', style: 'btn-default', size: 'auto', title: null, selectedTextFormat: 'values', width: false, container: false, hideDisabled: false, showSubtext: false, showIcon: true, showContent: true, dropupAuto: true, header: false, liveSearch: false, liveSearchPlaceholder: null, liveSearchNormalize: false, liveSearchStyle: 'contains', actionsBox: false, iconBase: 'glyphicon', tickIcon: 'glyphicon-ok', maxOptions: false, mobile: false, selectOnTab: false, dropdownAlignRight: false }; Selectpicker.prototype = { constructor: Selectpicker, init: function () { var that = this, id = this.$element.attr('id'); this.$element.addClass('bs-select-hidden'); // store originalIndex (key) and newIndex (value) in this.liObj for fast accessibility // allows us to do this.$lis.eq(that.liObj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]') this.liObj = {}; this.multiple = this.$element.prop('multiple'); this.autofocus = this.$element.prop('autofocus'); this.$newElement = this.createView(); this.$element.after(this.$newElement); this.$button = this.$newElement.children('button'); this.$menu = this.$newElement.children('.dropdown-menu'); this.$menuInner = this.$menu.children('.inner'); this.$searchbox = this.$menu.find('input'); if (this.options.dropdownAlignRight) this.$menu.addClass('dropdown-menu-right'); if (typeof id !== 'undefined') { this.$button.attr('data-id', id); $('label[for="' + id + '"]').click(function (e) { e.preventDefault(); that.$button.focus(); }); } this.checkDisabled(); this.clickListener(); if (this.options.liveSearch) this.liveSearchListener(); this.render(); this.setStyle(); this.setWidth(); if (this.options.container) this.selectPosition(); this.$menu.data('this', this); this.$newElement.data('this', this); if (this.options.mobile) this.mobile(); this.$newElement.on('hide.bs.dropdown', function (e) { that.$element.trigger('hide.bs.select', e); }); this.$newElement.on('hidden.bs.dropdown', function (e) { that.$element.trigger('hidden.bs.select', e); }); this.$newElement.on('show.bs.dropdown', function (e) { that.$element.trigger('show.bs.select', e); }); this.$newElement.on('shown.bs.dropdown', function (e) { that.$element.trigger('shown.bs.select', e); }); setTimeout(function () { that.$element.trigger('loaded.bs.select'); }); }, createDropdown: function () { // Options // If we are multiple, then add the show-tick class by default var multiple = this.multiple ? ' show-tick' : '', inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', autofocus = this.autofocus ? ' autofocus' : ''; // Elements var header = this.options.header ? '
                                  ' + this.options.header + '
                                  ' : ''; var searchbox = this.options.liveSearch ? '' : ''; var actionsbox = this.multiple && this.options.actionsBox ? '
                                  ' + '
                                  ' + '' + '' + '
                                  ' + '
                                  ' : ''; var donebutton = this.multiple && this.options.doneButton ? '
                                  ' + '
                                  ' + '' + '
                                  ' + '
                                  ' : ''; var drop = '
                                  ' + '' + '' + '
                                  '; return $(drop); }, createView: function () { var $drop = this.createDropdown(), li = this.createLi(); $drop.find('ul')[0].innerHTML = li; return $drop; }, reloadLi: function () { //Remove all children. this.destroyLi(); //Re build var li = this.createLi(); this.$menuInner[0].innerHTML = li; }, destroyLi: function () { this.$menu.find('li').remove(); }, createLi: function () { var that = this, _li = [], optID = 0, titleOption = document.createElement('option'), liIndex = -1; // increment liIndex whenever a new
                                • element is created to ensure liObj is correct // Helper functions /** * @param content * @param [index] * @param [classes] * @param [optgroup] * @returns {string} */ var generateLI = function (content, index, classes, optgroup) { return '' + content + '
                                • '; }; /** * @param text * @param [classes] * @param [inline] * @param [tokens] * @returns {string} */ var generateA = function (text, classes, inline, tokens) { return '' + text + '' + ''; }; if (this.options.title && !this.multiple) { // this option doesn't create a new
                                • element, but does add a new option, so liIndex is decreased // since liObj is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended liIndex--; if (!this.$element.find('.bs-title-option').length) { // Use native JS to prepend option (faster) var element = this.$element[0]; titleOption.className = 'bs-title-option'; titleOption.appendChild(document.createTextNode(this.options.title)); titleOption.value = ''; element.insertBefore(titleOption, element.firstChild); // Check if selected attribute is already set on an option. If not, select the titleOption option. if (element.options[element.selectedIndex].getAttribute('selected') === null) titleOption.selected = true; } } this.$element.find('option').each(function (index) { var $this = $(this); liIndex++; if ($this.hasClass('bs-title-option')) return; // Get the class and text for the option var optionClass = this.className || '', inline = this.style.cssText, text = $this.data('content') ? $this.data('content') : $this.html(), tokens = $this.data('tokens') ? $this.data('tokens') : null, subtext = typeof $this.data('subtext') !== 'undefined' ? '' + $this.data('subtext') + '' : '', icon = typeof $this.data('icon') !== 'undefined' ? ' ' : '', isDisabled = this.disabled || this.parentElement.tagName === 'OPTGROUP' && this.parentElement.disabled; if (icon !== '' && isDisabled) { icon = '' + icon + ''; } if (that.options.hideDisabled && isDisabled) { liIndex--; return; } if (!$this.data('content')) { // Prepend any icon and append any subtext to the main text. text = icon + '' + text + subtext + ''; } if (this.parentElement.tagName === 'OPTGROUP' && $this.data('divider') !== true) { if ($this.index() === 0) { // Is it the first option of the optgroup? optID += 1; // Get the opt group label var label = this.parentElement.label, labelSubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '' + $this.parent().data('subtext') + '' : '', labelIcon = $this.parent().data('icon') ? ' ' : '', optGroupClass = ' ' + this.parentElement.className || ''; label = labelIcon + '' + label + labelSubtext + ''; if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? liIndex++; _li.push(generateLI('', null, 'divider', optID + 'div')); } liIndex++; _li.push(generateLI(label, null, 'dropdown-header' + optGroupClass, optID)); } _li.push(generateLI(generateA(text, 'opt ' + optionClass + optGroupClass, inline, tokens), index, '', optID)); } else if ($this.data('divider') === true) { _li.push(generateLI('', index, 'divider')); } else if ($this.data('hidden') === true) { _li.push(generateLI(generateA(text, optionClass, inline, tokens), index, 'hidden is-hidden')); } else { if (this.previousElementSibling && this.previousElementSibling.tagName === 'OPTGROUP') { liIndex++; _li.push(generateLI('', null, 'divider', optID + 'div')); } _li.push(generateLI(generateA(text, optionClass, inline, tokens), index)); } that.liObj[index] = liIndex; }); //If we are not multiple, we don't have a selected item, and we don't have a title, select the first element so something is set in the button if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) { this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); } return _li.join(''); }, findLis: function () { if (this.$lis == null) this.$lis = this.$menu.find('li'); return this.$lis; }, /** * @param [updateLi] defaults to true */ render: function (updateLi) { var that = this, notDisabled; //Update the LI to match the SELECT if (updateLi !== false) { this.$element.find('option').each(function (index) { var $lis = that.findLis().eq(that.liObj[index]); that.setDisabled(index, this.disabled || this.parentElement.tagName === 'OPTGROUP' && this.parentElement.disabled, $lis); that.setSelected(index, this.selected, $lis); }); } this.tabIndex(); var selectedItems = this.$element.find('option').map(function () { if (this.selected) { if (that.options.hideDisabled && (this.disabled || this.parentElement.tagName === 'OPTGROUP' && this.parentElement.disabled)) return false; var $this = $(this), icon = $this.data('icon') && that.options.showIcon ? ' ' : '', subtext; if (that.options.showSubtext && $this.data('subtext') && !that.multiple) { subtext = ' ' + $this.data('subtext') + ''; } else { subtext = ''; } if (typeof $this.attr('title') !== 'undefined') { return $this.attr('title'); } else if ($this.data('content') && that.options.showContent) { return $this.data('content'); } else { return icon + $this.html() + subtext; } } }).toArray(); //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled //Convert all the values into a comma delimited string var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { var max = this.options.selectedTextFormat.split('>'); if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); } } if (this.options.title == undefined) { this.options.title = this.$element.attr('title'); } if (this.options.selectedTextFormat == 'static') { title = this.options.title; } //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text if (!title) { title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; } //strip all html-tags and trim the result this.$button.attr('title', $.trim(title.replace(/<[^>]*>?/g, ''))); this.$button.children('.filter-option').html(title); this.$element.trigger('rendered.bs.select'); }, /** * @param [style] * @param [status] */ setStyle: function (style, status) { if (this.$element.attr('class')) { this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); } var buttonClass = style ? style : this.options.style; if (status == 'add') { this.$button.addClass(buttonClass); } else if (status == 'remove') { this.$button.removeClass(buttonClass); } else { this.$button.removeClass(this.options.style); this.$button.addClass(buttonClass); } }, liHeight: function (refresh) { if (!refresh && (this.options.size === false || this.sizeInfo)) return; var newElement = document.createElement('div'), menu = document.createElement('div'), menuInner = document.createElement('ul'), divider = document.createElement('li'), li = document.createElement('li'), a = document.createElement('a'), text = document.createElement('span'), header = this.options.header ? this.$menu.find('.popover-title')[0].cloneNode(true) : null, search = this.options.liveSearch ? document.createElement('div') : null, actions = this.options.actionsBox && this.multiple ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, doneButton = this.options.doneButton && this.multiple ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null; text.className = 'text'; newElement.className = this.$menu[0].parentNode.className + ' open'; menu.className = 'dropdown-menu open'; menuInner.className = 'dropdown-menu inner'; divider.className = 'divider'; text.appendChild(document.createTextNode('Inner text')); a.appendChild(text); li.appendChild(a); menuInner.appendChild(li); menuInner.appendChild(divider); if (header) menu.appendChild(header); if (search) { // create a span instead of input as creating an input element is slower var input = document.createElement('span'); search.className = 'bs-searchbox'; input.className = 'form-control'; search.appendChild(input); menu.appendChild(search); } if (actions) menu.appendChild(actions); menu.appendChild(menuInner); if (doneButton) menu.appendChild(doneButton); newElement.appendChild(menu); document.body.appendChild(newElement); var liHeight = a.offsetHeight, headerHeight = header ? header.offsetHeight : 0, searchHeight = search ? search.offsetHeight : 0, actionsHeight = actions ? actions.offsetHeight : 0, doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, dividerHeight = $(divider).outerHeight(true), // fall back to jQuery if getComputedStyle is not supported menuStyle = getComputedStyle ? getComputedStyle(menu) : false, $menu = menuStyle ? $(menu) : null, menuPadding = parseInt(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + parseInt(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + parseInt(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + parseInt(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), menuExtras = menuPadding + parseInt(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + parseInt(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2; document.body.removeChild(newElement); this.sizeInfo = { liHeight: liHeight, headerHeight: headerHeight, searchHeight: searchHeight, actionsHeight: actionsHeight, doneButtonHeight: doneButtonHeight, dividerHeight: dividerHeight, menuPadding: menuPadding, menuExtras: menuExtras }; }, setSize: function () { this.findLis(); this.liHeight(); var that = this, $menu = this.$menu, $menuInner = this.$menuInner, $window = $(window), selectHeight = this.$newElement[0].offsetHeight, liHeight = this.sizeInfo['liHeight'], headerHeight = this.sizeInfo['headerHeight'], searchHeight = this.sizeInfo['searchHeight'], actionsHeight = this.sizeInfo['actionsHeight'], doneButtonHeight = this.sizeInfo['doneButtonHeight'], divHeight = this.sizeInfo['dividerHeight'], menuPadding = this.sizeInfo['menuPadding'], menuExtras = this.sizeInfo['menuExtras'], notDisabled = this.options.hideDisabled ? '.disabled' : '', menuHeight, getHeight, selectOffsetTop, selectOffsetBot, posVert = function () { selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; }; posVert(); if (this.options.header) $menu.css('padding-top', 0); if (this.options.size === 'auto') { var getSize = function () { var minHeight, hasClass = function (className, include) { return function (element) { if (include) { return (element.classList ? element.classList.contains(className) : $(element).hasClass(className)); } else { return !(element.classList ? element.classList.contains(className) : $(element).hasClass(className)); } }; }, lis = that.$menuInner[0].getElementsByTagName('li'), lisVisible = Array.prototype.filter ? Array.prototype.filter.call(lis, hasClass('hidden', false)) : that.$lis.not('.hidden'), optGroup = Array.prototype.filter ? Array.prototype.filter.call(lisVisible, hasClass('dropdown-header', true)) : lisVisible.filter('.dropdown-header'); posVert(); menuHeight = selectOffsetBot - menuExtras; if (that.options.container) { if (!$menu.data('height')) $menu.data('height', $menu.height()); getHeight = $menu.data('height'); } else { getHeight = $menu.height(); } if (that.options.dropupAuto) { that.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras) < getHeight); } if (that.$newElement.hasClass('dropup')) { menuHeight = selectOffsetTop - menuExtras; } if ((lisVisible.length + optGroup.length) > 3) { minHeight = liHeight * 3 + menuExtras - 2; } else { minHeight = 0; } $menu.css({ 'max-height': menuHeight + 'px', 'overflow': 'hidden', 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px' }); $menuInner.css({ 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding + 'px', 'overflow-y': 'auto', 'min-height': Math.max(minHeight - menuPadding, 0) + 'px' }); }; getSize(); this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); $window.off('resize.getSize scroll.getSize').on('resize.getSize scroll.getSize', getSize); } else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notDisabled).length > this.options.size) { var optIndex = this.$lis.not('.divider').not(notDisabled).children().slice(0, this.options.size).last().parent().index(), divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding; if (that.options.container) { if (!$menu.data('height')) $menu.data('height', $menu.height()); getHeight = $menu.data('height'); } else { getHeight = $menu.height(); } if (that.options.dropupAuto) { //noinspection JSUnusedAssignment this.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras) < getHeight); } $menu.css({ 'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px', 'overflow': 'hidden', 'min-height': '' }); $menuInner.css({ 'max-height': menuHeight - menuPadding + 'px', 'overflow-y': 'auto', 'min-height': '' }); } }, setWidth: function () { if (this.options.width === 'auto') { this.$menu.css('min-width', '0'); // Get correct width if element is hidden var $selectClone = this.$menu.parent().clone().appendTo('body'), $selectClone2 = this.options.container ? this.$newElement.clone().appendTo('body') : $selectClone, ulWidth = $selectClone.children('.dropdown-menu').outerWidth(), btnWidth = $selectClone2.css('width', 'auto').children('button').outerWidth(); $selectClone.remove(); $selectClone2.remove(); // Set width to whatever's larger, button title or longest option this.$newElement.css('width', Math.max(ulWidth, btnWidth) + 'px'); } else if (this.options.width === 'fit') { // Remove inline min-width so width can be changed from 'auto' this.$menu.css('min-width', ''); this.$newElement.css('width', '').addClass('fit-width'); } else if (this.options.width) { // Remove inline min-width so width can be changed from 'auto' this.$menu.css('min-width', ''); this.$newElement.css('width', this.options.width); } else { // Remove inline min-width/width so width can be changed this.$menu.css('min-width', ''); this.$newElement.css('width', ''); } // Remove fit-width class if width is changed programmatically if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { this.$newElement.removeClass('fit-width'); } }, selectPosition: function () { var that = this, drop = '
                                  ', $drop = $(drop), pos, actualHeight, getPlacement = function ($element) { $drop.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); pos = $element.offset(); actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; $drop.css({ 'top': pos.top + actualHeight, 'left': pos.left, 'width': $element[0].offsetWidth, 'position': 'absolute' }); }; this.$newElement.on('click', function () { if (that.isDisabled()) { return; } getPlacement($(this)); $drop.appendTo(that.options.container); $drop.toggleClass('open', !$(this).hasClass('open')); $drop.append(that.$menu); }); $(window).on('resize scroll', function () { getPlacement(that.$newElement); }); this.$element.on('hide.bs.select', function () { that.$menu.data('height', that.$menu.height()); $drop.detach(); }); }, setSelected: function (index, selected, $lis) { if (!$lis) { var $lis = this.findLis().eq(this.liObj[index]); } $lis.toggleClass('selected', selected); }, setDisabled: function (index, disabled, $lis) { if (!$lis) { var $lis = this.findLis().eq(this.liObj[index]); } if (disabled) { $lis.addClass('disabled').children('a').attr('href', '#').attr('tabindex', -1); } else { $lis.removeClass('disabled').children('a').removeAttr('href').attr('tabindex', 0); } }, isDisabled: function () { return this.$element[0].disabled; }, checkDisabled: function () { var that = this; if (this.isDisabled()) { this.$newElement.addClass('disabled'); this.$button.addClass('disabled').attr('tabindex', -1); } else { if (this.$button.hasClass('disabled')) { this.$newElement.removeClass('disabled'); this.$button.removeClass('disabled'); } if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { this.$button.removeAttr('tabindex'); } } this.$button.click(function () { return !that.isDisabled(); }); }, tabIndex: function () { if (this.$element.is('[tabindex]')) { this.$element.data('tabindex', this.$element.attr('tabindex')); this.$button.attr('tabindex', this.$element.data('tabindex')); } }, clickListener: function () { var that = this, $document = $(document); this.$newElement.on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); }); $document.data('spaceSelect', false); this.$button.on('keyup', function (e) { if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { e.preventDefault(); $document.data('spaceSelect', false); } }); this.$newElement.on('click', function () { that.setSize(); that.$element.on('shown.bs.select', function () { if (!that.options.liveSearch && !that.multiple) { that.$menu.find('.selected a').focus(); } else if (!that.multiple) { var selectedIndex = that.liObj[that.$element[0].selectedIndex]; if (typeof selectedIndex !== 'number') return; // scroll to selected option var offset = that.$lis.eq(selectedIndex)[0].offsetTop - that.$menuInner[0].offsetTop; offset = offset - that.$menuInner[0].offsetHeight/2 + that.sizeInfo.liHeight/2; that.$menuInner[0].scrollTop = offset; } }); }); this.$menu.on('click', 'li a', function (e) { var $this = $(this), clickedIndex = $this.parent().data('originalIndex'), prevValue = that.$element.val(), prevIndex = that.$element.prop('selectedIndex'); // Don't close on multi choice menu if (that.multiple) { e.stopPropagation(); } e.preventDefault(); //Don't run if we have been disabled if (!that.isDisabled() && !$this.parent().hasClass('disabled')) { var $options = that.$element.find('option'), $option = $options.eq(clickedIndex), state = $option.prop('selected'), $optgroup = $option.parent('optgroup'), maxOptions = that.options.maxOptions, maxOptionsGrp = $optgroup.data('maxOptions') || false; if (!that.multiple) { // Deselect all others if not multi select box $options.prop('selected', false); $option.prop('selected', true); that.$menu.find('.selected').removeClass('selected'); that.setSelected(clickedIndex, true); } else { // Toggle the one we have chosen if we are multi select. $option.prop('selected', !state); that.setSelected(clickedIndex, !state); $this.blur(); if (maxOptions !== false || maxOptionsGrp !== false) { var maxReached = maxOptions < $options.filter(':selected').length, maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { if (maxOptions && maxOptions == 1) { $options.prop('selected', false); $option.prop('selected', true); that.$menu.find('.selected').removeClass('selected'); that.setSelected(clickedIndex, true); } else if (maxOptionsGrp && maxOptionsGrp == 1) { $optgroup.find('option:selected').prop('selected', false); $option.prop('selected', true); var optgroupID = $this.parent().data('optgroup'); that.$menu.find('[data-optgroup="' + optgroupID + '"]').removeClass('selected'); that.setSelected(clickedIndex, true); } else { var maxOptionsArr = (typeof that.options.maxOptionsText === 'function') ? that.options.maxOptionsText(maxOptions, maxOptionsGrp) : that.options.maxOptionsText, maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), $notify = $('
                                  '); // If {var} is set in array, replace it /** @deprecated */ if (maxOptionsArr[2]) { maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); } $option.prop('selected', false); that.$menu.append($notify); if (maxOptions && maxReached) { $notify.append($('
                                  ' + maxTxt + '
                                  ')); that.$element.trigger('maxReached.bs.select'); } if (maxOptionsGrp && maxReachedGrp) { $notify.append($('
                                  ' + maxTxtGrp + '
                                  ')); that.$element.trigger('maxReachedGrp.bs.select'); } setTimeout(function () { that.setSelected(clickedIndex, false); }, 10); $notify.delay(750).fadeOut(300, function () { $(this).remove(); }); } } } } if (!that.multiple) { that.$button.focus(); } else if (that.options.liveSearch) { that.$searchbox.focus(); } // Trigger select 'change' if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { that.$element.change(); // $option.prop('selected') is current option state (selected/unselected). state is previous option state. that.$element.trigger('changed.bs.select', [clickedIndex, $option.prop('selected'), state]); } } }); this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) { if (e.currentTarget == this) { e.preventDefault(); e.stopPropagation(); if (that.options.liveSearch && !$(e.target).hasClass('close')) { that.$searchbox.focus(); } else { that.$button.focus(); } } }); this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) { e.preventDefault(); e.stopPropagation(); if (that.options.liveSearch) { that.$searchbox.focus(); } else { that.$button.focus(); } }); this.$menu.on('click', '.popover-title .close', function () { that.$button.click(); }); this.$searchbox.on('click', function (e) { e.stopPropagation(); }); this.$menu.on('click', '.actions-btn', function (e) { if (that.options.liveSearch) { that.$searchbox.focus(); } else { that.$button.focus(); } e.preventDefault(); e.stopPropagation(); if ($(this).hasClass('bs-select-all')) { that.selectAll(); } else { that.deselectAll(); } that.$element.change(); }); this.$element.change(function () { that.render(false); }); }, liveSearchListener: function () { var that = this, $no_results = $('
                                • '); this.$newElement.on('click.dropdown.data-api touchstart.dropdown.data-api', function () { that.$menuInner.find('.active').removeClass('active'); if (!!that.$searchbox.val()) { that.$searchbox.val(''); that.$lis.not('.is-hidden').removeClass('hidden'); if (!!$no_results.parent().length) $no_results.remove(); } if (!that.multiple) that.$menuInner.find('.selected').addClass('active'); setTimeout(function () { that.$searchbox.focus(); }, 10); }); this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function (e) { e.stopPropagation(); }); this.$searchbox.on('input propertychange', function () { if (that.$searchbox.val()) { var $searchBase = that.$lis.not('.is-hidden').removeClass('hidden').children('a'); if (that.options.liveSearchNormalize) { $searchBase = $searchBase.not(':a' + that._searchStyle() + '(' + normalizeToBase(that.$searchbox.val()) + ')'); } else { $searchBase = $searchBase.not(':' + that._searchStyle() + '(' + that.$searchbox.val() + ')'); } $searchBase.parent().addClass('hidden'); that.$lis.filter('.dropdown-header').each(function () { var $this = $(this), optgroup = $this.data('optgroup'); if (that.$lis.filter('[data-optgroup=' + optgroup + ']').not($this).not('.hidden').length === 0) { $this.addClass('hidden'); that.$lis.filter('[data-optgroup=' + optgroup + 'div]').addClass('hidden'); } }); var $lisVisible = that.$lis.not('.hidden'); // hide divider if first or last visible, or if followed by another divider $lisVisible.each(function (index) { var $this = $(this); if ($this.hasClass('divider') && ( $this.index() === $lisVisible.eq(0).index() || $this.index() === $lisVisible.last().index() || $lisVisible.eq(index + 1).hasClass('divider'))) { $this.addClass('hidden'); } }); if (!that.$lis.not('.hidden, .no-results').length) { if (!!$no_results.parent().length) { $no_results.remove(); } $no_results.html(that.options.noneResultsText.replace('{0}', '"' + htmlEscape(that.$searchbox.val()) + '"')).show(); that.$menuInner.append($no_results); } else if (!!$no_results.parent().length) { $no_results.remove(); } } else { that.$lis.not('.is-hidden').removeClass('hidden'); if (!!$no_results.parent().length) { $no_results.remove(); } } that.$lis.filter('.active').removeClass('active'); that.$lis.not('.hidden, .divider, .dropdown-header').eq(0).addClass('active').children('a').focus(); $(this).focus(); }); }, _searchStyle: function () { var style = 'icontains'; switch (this.options.liveSearchStyle) { case 'begins': case 'startsWith': style = 'ibegins'; break; case 'contains': default: break; //no need to change the default } return style; }, val: function (value) { if (typeof value !== 'undefined') { this.$element.val(value); this.render(); return this.$element; } else { return this.$element.val(); } }, selectAll: function () { this.findLis(); this.$element.find('option:enabled').not('[data-divider], [data-hidden]').prop('selected', true); this.$lis.not('.divider, .dropdown-header, .disabled, .hidden').addClass('selected'); this.render(false); }, deselectAll: function () { this.findLis(); this.$element.find('option:enabled').not('[data-divider], [data-hidden]').prop('selected', false); this.$lis.not('.divider, .dropdown-header, .disabled, .hidden').removeClass('selected'); this.render(false); }, keydown: function (e) { var $this = $(this), $parent = $this.is('input') ? $this.parent().parent() : $this.parent(), $items, that = $parent.data('this'), index, next, first, last, prev, nextPrev, prevIndex, isActive, selector = ':not(.disabled, .hidden, .dropdown-header, .divider)', keyCodeMap = { 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9' }; if (that.options.liveSearch) $parent = $this.parent().parent(); if (that.options.container) $parent = that.$menu; $items = $('[role=menu] li a', $parent); isActive = that.$menu.parent().hasClass('open'); if (!isActive && (e.keyCode >= 48 && e.keyCode <= 57 || event.keyCode >= 65 && event.keyCode <= 90)) { if (!that.options.container) { that.setSize(); that.$menu.parent().addClass('open'); isActive = true; } else { that.$newElement.trigger('click'); } that.$searchbox.focus(); } if (that.options.liveSearch) { if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && that.$menu.find('.active').length === 0) { e.preventDefault(); that.$menu.parent().removeClass('open'); if (that.options.container) that.$newElement.removeClass('open'); that.$button.focus(); } // $items contains li elements when liveSearch is enabled $items = $('[role=menu] li:not(.disabled, .hidden, .dropdown-header, .divider)', $parent); if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { if ($items.filter('.active').length === 0) { $items = that.$newElement.find('li'); if (that.options.liveSearchNormalize) { $items = $items.filter(':a' + that._searchStyle() + '(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); } else { $items = $items.filter(':' + that._searchStyle() + '(' + keyCodeMap[e.keyCode] + ')'); } } } } if (!$items.length) return; if (/(38|40)/.test(e.keyCode.toString(10))) { index = $items.index($items.filter(':focus')); first = $items.parent(selector).first().data('originalIndex'); last = $items.parent(selector).last().data('originalIndex'); next = $items.eq(index).parent().nextAll(selector).eq(0).data('originalIndex'); prev = $items.eq(index).parent().prevAll(selector).eq(0).data('originalIndex'); nextPrev = $items.eq(next).parent().prevAll(selector).eq(0).data('originalIndex'); if (that.options.liveSearch) { $items.each(function (i) { if (!$(this).hasClass('disabled')) { $(this).data('index', i); } }); index = $items.index($items.filter('.active')); first = $items.first().data('index'); last = $items.last().data('index'); next = $items.eq(index).nextAll().eq(0).data('index'); prev = $items.eq(index).prevAll().eq(0).data('index'); nextPrev = $items.eq(next).prevAll().eq(0).data('index'); } prevIndex = $this.data('prevIndex'); if (e.keyCode == 38) { if (that.options.liveSearch) index -= 1; if (index != nextPrev && index > prev) index = prev; if (index < first) index = first; if (index == prevIndex) index = last; } else if (e.keyCode == 40) { if (that.options.liveSearch) index += 1; if (index == -1) index = 0; if (index != nextPrev && index < next) index = next; if (index > last) index = last; if (index == prevIndex) index = first; } $this.data('prevIndex', index); if (!that.options.liveSearch) { $items.eq(index).focus(); } else { e.preventDefault(); if (!$this.hasClass('dropdown-toggle')) { $items.removeClass('active').eq(index).addClass('active').children('a').focus(); $this.focus(); } } } else if (!$this.is('input')) { var keyIndex = [], count, prevKey; $items.each(function () { if (!$(this).parent().hasClass('disabled')) { if ($.trim($(this).text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { keyIndex.push($(this).parent().index()); } } }); count = $(document).data('keycount'); count++; $(document).data('keycount', count); prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1); if (prevKey != keyCodeMap[e.keyCode]) { count = 1; $(document).data('keycount', count); } else if (count >= keyIndex.length) { $(document).data('keycount', 0); if (count > keyIndex.length) count = 1; } $items.eq(keyIndex[count - 1]).focus(); } // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); if (!that.options.liveSearch) { var elem = $(':focus'); elem.click(); // Bring back focus for multiselects elem.focus(); // Prevent screen from scrolling if the user hit the spacebar e.preventDefault(); // Fixes spacebar selection of dropdown items in FF & IE $(document).data('spaceSelect', true); } else if (!/(32)/.test(e.keyCode.toString(10))) { that.$menu.find('.active a').click(); $this.focus(); } $(document).data('keycount', 0); } if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { that.$menu.parent().removeClass('open'); if (that.options.container) that.$newElement.removeClass('open'); that.$button.focus(); } }, mobile: function () { this.$element.addClass('mobile-device').appendTo(this.$newElement); if (this.options.container) this.$menu.hide(); }, refresh: function () { this.$lis = null; this.reloadLi(); this.render(); this.checkDisabled(); this.liHeight(true); this.setStyle(); this.setWidth(); if (this.$lis) this.$searchbox.trigger('propertychange'); this.$element.trigger('refreshed.bs.select'); }, hide: function () { this.$newElement.hide(); }, show: function () { this.$newElement.show(); }, remove: function () { this.$newElement.remove(); this.$element.remove(); } }; // SELECTPICKER PLUGIN DEFINITION // ============================== function Plugin(option, event) { // get the args of the outer function.. var args = arguments; // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them // to get lost/corrupted in android 2.3 and IE9 #715 #775 var _option = option, _event = event; [].shift.apply(args); var value; var chain = this.each(function () { var $this = $(this); if ($this.is('select')) { var data = $this.data('selectpicker'), options = typeof _option == 'object' && _option; if (!data) { var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); $this.data('selectpicker', (data = new Selectpicker(this, config, _event))); } else if (options) { for (var i in options) { if (options.hasOwnProperty(i)) { data.options[i] = options[i]; } } } if (typeof _option == 'string') { if (data[_option] instanceof Function) { value = data[_option].apply(data, args); } else { value = data.options[_option]; } } } }); if (typeof value !== 'undefined') { //noinspection JSUnusedAssignment return value; } else { return chain; } } var old = $.fn.selectpicker; $.fn.selectpicker = Plugin; $.fn.selectpicker.Constructor = Selectpicker; // SELECTPICKER NO CONFLICT // ======================== $.fn.selectpicker.noConflict = function () { $.fn.selectpicker = old; return this; }; $(document) .data('keycount', 0) .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="menu"], .bs-searchbox input', Selectpicker.prototype.keydown) .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="menu"], .bs-searchbox input', function (e) { e.stopPropagation(); }); // SELECTPICKER DATA-API // ===================== $(window).on('load.bs.select.data-api', function () { $('.selectpicker').each(function () { var $selectpicker = $(this); Plugin.call($selectpicker, $selectpicker.data()); }) }); })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-bg_BG.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Нищо избрано', noneResultsText: 'Няма резултат за {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} избран елемент" : "{0} избрани елемента"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Лимита е достигнат ({n} елемент максимум)' : 'Лимита е достигнат ({n} елемента максимум)', (numGroup == 1) ? 'Груповия лимит е достигнат ({n} елемент максимум)' : 'Груповия лимит е достигнат ({n} елемента максимум)' ]; }, selectAllText: 'Избери всички', deselectAllText: 'Размаркирай всички', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-cs_CZ.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nic není vybráno', noneResultsText: 'Žádné výsledky {0}', countSelectedText: 'Označeno {0} z {1}', maxOptionsText: ['Limit překročen ({n} {var} max)', 'Limit skupiny překročen ({n} {var} max)', ['položek', 'položka']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-da_DK.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Intet valgt', noneResultsText: 'Ingen resultater fundet {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} valgt" : "{0} valgt"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Begrænsning nået (max {n} valgt)' : 'Begrænsning nået (max {n} valgte)', (numGroup == 1) ? 'Gruppe-begrænsning nået (max {n} valgt)' : 'Gruppe-begrænsning nået (max {n} valgte)' ]; }, selectAllText: 'Markér alle', deselectAllText: 'Afmarkér alle', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-de_DE.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Bitte wählen...', noneResultsText: 'Keine Ergebnisse für {0}', countSelectedText: '{0} von {1} ausgewählt', maxOptionsText: ['Limit erreicht ({n} {var} max.)', 'Gruppen-Limit erreicht ({n} {var} max.)', ['Eintrag', 'Einträge']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-en_US.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nothing selected', noneResultsText: 'No results match {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' ]; }, selectAllText: 'Select All', deselectAllText: 'Deselect All', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-es_CL.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'No hay selección', noneResultsText: 'No hay resultados {0}', countSelectedText: 'Seleccionados {0} de {1}', maxOptionsText: ['Límite alcanzado ({n} {var} max)', 'Límite del grupo alcanzado({n} {var} max)', ['elementos', 'element']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-eu.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Hautapenik ez', noneResultsText: 'Emaitzarik ez {0}', countSelectedText: '{1}(e)tik {0} hautatuta', maxOptionsText: ['Mugara iritsita ({n} {var} gehienez)', 'Taldearen mugara iritsita ({n} {var} gehienez)', ['elementu', 'elementu']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-fa_IR.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'چیزی انتخاب نشده است', noneResultsText: 'هیج مشابهی برای {0} پیدا نشد', countSelectedText: "{0} از {1} مورد انتخاب شده", maxOptionsText: ['بیشتر ممکن نیست {حداکثر {n} عدد}', 'بیشتر ممکن نیست {حداکثر {n} عدد}'], selectAllText: 'انتخاب همه', deselectAllText: 'انتخاب هیچ کدام', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-fr_FR.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Aucune sélection', noneResultsText: 'Aucun résultat pour {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected > 1) ? "{0} éléments sélectionnés" : "{0} élément sélectionné"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll > 1) ? 'Limite atteinte ({n} éléments max)' : 'Limite atteinte ({n} élément max)', (numGroup > 1) ? 'Limite du groupe atteinte ({n} éléments max)' : 'Limite du groupe atteinte ({n} élément max)' ]; }, multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-hu_HU.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Válasszon!', noneResultsText: 'Nincs találat {0}', countSelectedText: function (numSelected, numTotal) { return '{n} elem kiválasztva'; }, maxOptionsText: function (numAll, numGroup) { return [ 'Legfeljebb {n} elem választható', 'A csoportban legfeljebb {n} elem választható' ]; }, selectAllText: 'Mind', deselectAllText: 'Egyik sem', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-it_IT.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nessuna selezione', noneResultsText: 'Nessun risultato per {0}', countSelectedText: 'Selezionati {0} di {1}', maxOptionsText: ['Limite raggiunto ({n} {var} max)', 'Limite del gruppo raggiunto ({n} {var} max)', ['elementi', 'elemento']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-ko_KR.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '항목을 선택해주세요', noneResultsText: '{0} 검색 결과가 없습니다', countSelectedText: function (numSelected, numTotal) { return "{0}개를 선택하였습니다"; }, maxOptionsText: function (numAll, numGroup) { return [ '{n}개까지 선택 가능합니다', '해당 그룹은 {n}개까지 선택 가능합니다' ]; }, selectAllText: '전체선택', deselectAllText: '전체해제', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-nl_NL.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Niets geselecteerd', noneResultsText: 'Geen resultaten gevonden voor {0}', countSelectedText: '{0} van {1} geselecteerd', maxOptionsText: ['Limiet bereikt ({n} {var} max)', 'Groep limiet bereikt ({n} {var} max)', ['items', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-pl_PL.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nic nie zaznaczono', noneResultsText: 'Brak wyników wyszukiwania {0}', countSelectedText: 'Zaznaczono {0} z {1}', maxOptionsText: ['Osiągnięto limit ({n} {var} max)', 'Limit grupy osiągnięty ({n} {var} max)', ['elementy', 'element']], selectAll: 'Zaznacz wszystkie', deselectAll: 'Odznacz wszystkie', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-pt_BR.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nada selecionado', noneResultsText: 'Nada encontrado contendo {0}', countSelectedText: 'Selecionado {0} de {1}', maxOptionsText: ['Limite excedido (máx. {n} {var})', 'Limite do grupo excedido (máx. {n} {var})', ['itens', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-pt_PT.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nenhum seleccionado', noneResultsText: 'Sem resultados contendo {0}', countSelectedText: 'Selecionado {0} de {1}', maxOptionsText: ['Limite ultrapassado (máx. {n} {var})', 'Limite de seleções ultrapassado (máx. {n} {var})', ['itens', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-ro_RO.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nu a fost selectat nimic', noneResultsText: 'Nu exista niciun rezultat {0}', countSelectedText: '{0} din {1} selectat(e)', maxOptionsText: ['Limita a fost atinsa ({n} {var} max)', 'Limita de grup a fost atinsa ({n} {var} max)', ['iteme', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-ru_RU.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Ничего не выбрано', noneResultsText: 'Совпадений не найдено {0}', countSelectedText: 'Выбрано {0} из {1}', maxOptionsText: ['Достигнут предел ({n} {var} максимум)', 'Достигнут предел в группе ({n} {var} максимум)', ['items', 'item']], doneButtonText: 'Закрыть', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-sk_SK.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Vyberte zo zoznamu', noneResultsText: 'Pre výraz {0} neboli nájdené žiadne výsledky', countSelectedText: 'Vybrané {0} z {1}', maxOptionsText: ['Limit prekročený ({n} {var} max)', 'Limit skupiny prekročený ({n} {var} max)', ['položiek', 'položka']], selectAllText: 'Vybrať všetky', deselectAllText: 'Zrušiť výber', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-sl_SI.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nič izbranega', noneResultsText: 'Ni zadetkov za {0}', countSelectedText: function (numSelected, numTotal) { "Število izbranih: {0}"; }, maxOptionsText: function (numAll, numGroup) { return [ 'Omejitev dosežena (max. izbranih: {n})', 'Omejitev skupine dosežena (max. izbranih: {n})' ]; }, selectAllText: 'Izberi vse', deselectAllText: 'Počisti izbor', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-sv_SE.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Inget valt', noneResultsText: 'Inget sökresultat matchar {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected === 1) ? "{0} alternativ valt" : "{0} alternativ valda"; }, maxOptionsText: function (numAll, numGroup) { return [ 'Gräns uppnåd (max {n} alternativ)', 'Gräns uppnåd (max {n} gruppalternativ)' ]; }, selectAllText: 'Markera alla', deselectAllText: 'Avmarkera alla', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-tr_TR.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Hiçbiri seçilmedi', noneResultsText: 'Hiçbir sonuç bulunamadı {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} öğe seçildi" : "{0} öğe seçildi"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit aşıldı (maksimum {n} sayıda öğe )' : 'Limit aşıldı (maksimum {n} sayıda öğe)', (numGroup == 1) ? 'Grup limiti aşıldı (maksimum {n} sayıda öğe)' : 'Grup limiti aşıldı (maksimum {n} sayıda öğe)' ]; }, selectAllText: 'Tümünü Seç', deselectAllText: 'Seçiniz', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-ua_UA.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Нічого не вибрано', noneResultsText: 'Збігів не знайдено {0}', countSelectedText: 'Вибрано {0} із {1}', maxOptionsText: ['Досягнута межа ({n} {var} максимум)', 'Досягнута межа в групі ({n} {var} максимум)', ['items', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-zh_CN.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '没有选中任何项', noneResultsText: '没有找到匹配项', countSelectedText: '选中{1}中的{0}项', maxOptionsText: ['超出限制 (最多选择{n}项)', '组选择超出限制(最多选择{n}组)'], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-select/1.7.2/js/i18n/defaults-zh_TW.js ================================================ /*! * Bootstrap-select v1.7.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2015 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function () { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '沒有選取任何項目', noneResultsText: '沒有找到符合的結果', countSelectedText: '已經選取{0}個項目', maxOptionsText: ['超過限制 (最多選擇{n}項)', '超過限制(最多選擇{n}組)'], selectAllText: '選取全部', deselectAllText: '全部取消', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-sweetalert/lib/sweet-alert-animations.less ================================================ @-webkit-keyframes showSweetAlert { 0% { transform: scale(0.7); -webkit-transform: scale(0.7); } 45% { transform: scale(1.05); -webkit-transform: scale(1.05); } 80% { transform: scale(0.95); -webkit-tranform: scale(0.95); } 100% { transform: scale(1); -webkit-transform: scale(1); } } @keyframes showSweetAlert { 0% { transform: scale(0.7); -webkit-transform: scale(0.7); } 45% { transform: scale(1.05); -webkit-transform: scale(1.05); } 80% { transform: scale(0.95); -webkit-tranform: scale(0.95); } 100% { transform: scale(1); -webkit-transform: scale(1); } } @-webkit-keyframes hideSweetAlert { 0% { transform: scale(1); -webkit-transform: scale(1); } 100% { transform: scale(0.5); -webkit-transform: scale(0.5); } } @keyframes hideSweetAlert { 0% { transform: scale(1); -webkit-transform: scale(1); } 100% { transform: scale(0.5); -webkit-transform: scale(0.5); } } .showSweetAlert { -webkit-animation: showSweetAlert 0.3s; animation: showSweetAlert 0.3s; } .hideSweetAlert { -webkit-animation: hideSweetAlert 0.2s; animation: hideSweetAlert 0.2s; } @-webkit-keyframes animateSuccessTip { 0% { width: 0; left: 1px; top: 19px; } 54% { width: 0; left: 1px; top: 19px; } 70% { width: 50px; left: -8px; top: 37px; } 84% { width: 17px; left: 21px; top: 48px; } 100% { width: 25px; left: 14px; top: 45px; } } @keyframes animateSuccessTip { 0% { width: 0; left: 1px; top: 19px; } 54% { width: 0; left: 1px; top: 19px; } 70% { width: 50px; left: -8px; top: 37px; } 84% { width: 17px; left: 21px; top: 48px; } 100% { width: 25px; left: 14px; top: 45px; } } @-webkit-keyframes animateSuccessLong { 0% { width: 0; right: 46px; top: 54px; } 65% { width: 0; right: 46px; top: 54px; } 84% { width: 55px; right: 0px; top: 35px; } 100% { width: 47px; right: 8px; top: 38px; } } @keyframes animateSuccessLong { 0% { width: 0; right: 46px; top: 54px; } 65% { width: 0; right: 46px; top: 54px; } 84% { width: 55px; right: 0px; top: 35px; } 100% { width: 47px; right: 8px; top: 38px; } } @-webkit-keyframes rotatePlaceholder { 0% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 5% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 12% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } 100% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } } @keyframes rotatePlaceholder { 0% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 5% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 12% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } 100% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } } .animateSuccessTip { -webkit-animation: animateSuccessTip 0.75s; animation: animateSuccessTip 0.75s; } .animateSuccessLong { -webkit-animation: animateSuccessLong 0.75s; animation: animateSuccessLong 0.75s; } .icon.success.animate::after { -webkit-animation: rotatePlaceholder 4.25s ease-in; animation: rotatePlaceholder 4.25s ease-in; } @-webkit-keyframes animateErrorIcon { 0% { transform: rotateX(100deg); -webkit-transform: rotateX(100deg); opacity: 0; } 100% { transform: rotateX(0deg); -webkit-transform: rotateX(0deg); opacity: 1; } } @keyframes animateErrorIcon { 0% { transform: rotateX(100deg); -webkit-transform: rotateX(100deg); opacity: 0; } 100% { transform: rotateX(0deg); -webkit-transform: rotateX(0deg); opacity: 1; } } .animateErrorIcon { -webkit-animation: animateErrorIcon 0.5s; animation: animateErrorIcon 0.5s; } @-webkit-keyframes animateXMark { 0% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 50% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 80% { transform: scale(1.15); -webkit-transform: scale(1.15); margin-top: -6px; } 100% { transform: scale(1); -webkit-transform: scale(1); margin-top: 0; opacity: 1; } } @keyframes animateXMark { 0% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 50% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 80% { transform: scale(1.15); -webkit-transform: scale(1.15); margin-top: -6px; } 100% { transform: scale(1); -webkit-transform: scale(1); margin-top: 0; opacity: 1; } } .animateXMark { -webkit-animation: animateXMark 0.5s; animation: animateXMark 0.5s; } @-webkit-keyframes pulseWarning { 0% { border-color: #F8D486; } 100% { border-color: #F8BB86; } } @keyframes pulseWarning { 0% { border-color: #F8D486; } 100% { border-color: #F8BB86; } } .pulseWarning { -webkit-animation: pulseWarning 0.75s infinite alternate; animation: pulseWarning 0.75s infinite alternate; } @-webkit-keyframes pulseWarningIns { 0% { background-color: #F8D486; } 100% { background-color: #F8BB86; } } @keyframes pulseWarningIns { 0% { background-color: #F8D486; } 100% { background-color: #F8BB86; } } .pulseWarningIns { -webkit-animation: pulseWarningIns 0.75s infinite alternate; animation: pulseWarningIns 0.75s infinite alternate; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-sweetalert/lib/sweet-alert-combine.less ================================================ @import "../bootstrap/variables"; @import "../bootstrap/mixins"; @import "sweet-alert"; ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-sweetalert/lib/sweet-alert.css ================================================ @-webkit-keyframes showSweetAlert { 0% { transform: scale(0.7); -webkit-transform: scale(0.7); } 45% { transform: scale(1.05); -webkit-transform: scale(1.05); } 80% { transform: scale(0.95); -webkit-tranform: scale(0.95); } 100% { transform: scale(1); -webkit-transform: scale(1); } } @keyframes showSweetAlert { 0% { transform: scale(0.7); -webkit-transform: scale(0.7); } 45% { transform: scale(1.05); -webkit-transform: scale(1.05); } 80% { transform: scale(0.95); -webkit-tranform: scale(0.95); } 100% { transform: scale(1); -webkit-transform: scale(1); } } @-webkit-keyframes hideSweetAlert { 0% { transform: scale(1); -webkit-transform: scale(1); } 100% { transform: scale(0.5); -webkit-transform: scale(0.5); } } @keyframes hideSweetAlert { 0% { transform: scale(1); -webkit-transform: scale(1); } 100% { transform: scale(0.5); -webkit-transform: scale(0.5); } } .showSweetAlert { -webkit-animation: showSweetAlert 0.3s; animation: showSweetAlert 0.3s; } .hideSweetAlert { -webkit-animation: hideSweetAlert 0.2s; animation: hideSweetAlert 0.2s; } @-webkit-keyframes animateSuccessTip { 0% { width: 0; left: 1px; top: 19px; } 54% { width: 0; left: 1px; top: 19px; } 70% { width: 50px; left: -8px; top: 37px; } 84% { width: 17px; left: 21px; top: 48px; } 100% { width: 25px; left: 14px; top: 45px; } } @keyframes animateSuccessTip { 0% { width: 0; left: 1px; top: 19px; } 54% { width: 0; left: 1px; top: 19px; } 70% { width: 50px; left: -8px; top: 37px; } 84% { width: 17px; left: 21px; top: 48px; } 100% { width: 25px; left: 14px; top: 45px; } } @-webkit-keyframes animateSuccessLong { 0% { width: 0; right: 46px; top: 54px; } 65% { width: 0; right: 46px; top: 54px; } 84% { width: 55px; right: 0px; top: 35px; } 100% { width: 47px; right: 8px; top: 38px; } } @keyframes animateSuccessLong { 0% { width: 0; right: 46px; top: 54px; } 65% { width: 0; right: 46px; top: 54px; } 84% { width: 55px; right: 0px; top: 35px; } 100% { width: 47px; right: 8px; top: 38px; } } @-webkit-keyframes rotatePlaceholder { 0% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 5% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 12% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } 100% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } } @keyframes rotatePlaceholder { 0% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 5% { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } 12% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } 100% { transform: rotate(-405deg); -webkit-transform: rotate(-405deg); } } .animateSuccessTip { -webkit-animation: animateSuccessTip 0.75s; animation: animateSuccessTip 0.75s; } .animateSuccessLong { -webkit-animation: animateSuccessLong 0.75s; animation: animateSuccessLong 0.75s; } .icon.success.animate::after { -webkit-animation: rotatePlaceholder 4.25s ease-in; animation: rotatePlaceholder 4.25s ease-in; } @-webkit-keyframes animateErrorIcon { 0% { transform: rotateX(100deg); -webkit-transform: rotateX(100deg); opacity: 0; } 100% { transform: rotateX(0deg); -webkit-transform: rotateX(0deg); opacity: 1; } } @keyframes animateErrorIcon { 0% { transform: rotateX(100deg); -webkit-transform: rotateX(100deg); opacity: 0; } 100% { transform: rotateX(0deg); -webkit-transform: rotateX(0deg); opacity: 1; } } .animateErrorIcon { -webkit-animation: animateErrorIcon 0.5s; animation: animateErrorIcon 0.5s; } @-webkit-keyframes animateXMark { 0% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 50% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 80% { transform: scale(1.15); -webkit-transform: scale(1.15); margin-top: -6px; } 100% { transform: scale(1); -webkit-transform: scale(1); margin-top: 0; opacity: 1; } } @keyframes animateXMark { 0% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 50% { transform: scale(0.4); -webkit-transform: scale(0.4); margin-top: 26px; opacity: 0; } 80% { transform: scale(1.15); -webkit-transform: scale(1.15); margin-top: -6px; } 100% { transform: scale(1); -webkit-transform: scale(1); margin-top: 0; opacity: 1; } } .animateXMark { -webkit-animation: animateXMark 0.5s; animation: animateXMark 0.5s; } @-webkit-keyframes pulseWarning { 0% { border-color: #F8D486; } 100% { border-color: #F8BB86; } } @keyframes pulseWarning { 0% { border-color: #F8D486; } 100% { border-color: #F8BB86; } } .pulseWarning { -webkit-animation: pulseWarning 0.75s infinite alternate; animation: pulseWarning 0.75s infinite alternate; } @-webkit-keyframes pulseWarningIns { 0% { background-color: #F8D486; } 100% { background-color: #F8BB86; } } @keyframes pulseWarningIns { 0% { background-color: #F8D486; } 100% { background-color: #F8BB86; } } .pulseWarningIns { -webkit-animation: pulseWarningIns 0.75s infinite alternate; animation: pulseWarningIns 0.75s infinite alternate; } .sweet-overlay { background-color: rgba(0, 0, 0, 0.4); position: fixed; left: 0; right: 0; top: 0; bottom: 0; display: none; z-index: 1040; } .sweet-alert { background-color: #ffffff; width: 478px; padding: 17px; border-radius: 5px; text-align: center; position: fixed; left: 50%; top: 50%; margin-left: -256px; margin-top: -200px; overflow: hidden; display: none; z-index: 2000; } @media all and (max-width: 767px) { .sweet-alert { width: auto; margin-left: 0; margin-right: 0; left: 15px; right: 15px; } } .sweet-alert .icon { width: 80px; height: 80px; border: 4px solid gray; border-radius: 50%; margin: 20px auto; position: relative; box-sizing: content-box; } .sweet-alert .icon.error { border-color: #d43f3a; } .sweet-alert .icon.error .x-mark { position: relative; display: block; } .sweet-alert .icon.error .line { position: absolute; height: 5px; width: 47px; background-color: #d9534f; display: block; top: 37px; border-radius: 2px; } .sweet-alert .icon.error .line.left { -webkit-transform: rotate(45deg); transform: rotate(45deg); left: 17px; } .sweet-alert .icon.error .line.right { -webkit-transform: rotate(-45deg); transform: rotate(-45deg); right: 16px; } .sweet-alert .icon.warning { border-color: #eea236; } .sweet-alert .icon.warning .body { position: absolute; width: 5px; height: 47px; left: 50%; top: 10px; border-radius: 2px; margin-left: -2px; background-color: #f0ad4e; } .sweet-alert .icon.warning .dot { position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -3px; left: 50%; bottom: 10px; background-color: #f0ad4e; } .sweet-alert .icon.info { border-color: #46b8da; } .sweet-alert .icon.info::before { content: ""; position: absolute; width: 5px; height: 29px; left: 50%; bottom: 17px; border-radius: 2px; margin-left: -2px; background-color: #5bc0de; } .sweet-alert .icon.info::after { content: ""; position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -3px; top: 19px; background-color: #5bc0de; } .sweet-alert .icon.success { border-color: #4cae4c; } .sweet-alert .icon.success::before, .sweet-alert .icon.success::after { content: ''; border-radius: 50%; position: absolute; width: 60px; height: 120px; background: white; -webkit-transform: rotate(45deg); transform: rotate(45deg); } .sweet-alert .icon.success::before { border-radius: 120px 0 0 120px; top: -7px; left: -33px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: 60px 60px; transform-origin: 60px 60px; } .sweet-alert .icon.success::after { border-radius: 0 120px 120px 0; top: -11px; left: 30px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: 0px 60px; transform-origin: 0px 60px; } .sweet-alert .icon.success .placeholder { width: 80px; height: 80px; border: 4px solid rgba(92, 184, 92, 0.2); border-radius: 50%; box-sizing: content-box; position: absolute; left: -4px; top: -4px; z-index: 2; } .sweet-alert .icon.success .fix { width: 5px; height: 90px; background-color: #ffffff; position: absolute; left: 28px; top: 8px; z-index: 1; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); } .sweet-alert .icon.success .line { height: 5px; background-color: #5cb85c; display: block; border-radius: 2px; position: absolute; z-index: 2; } .sweet-alert .icon.success .line.tip { width: 25px; left: 14px; top: 46px; -webkit-transform: rotate(45deg); transform: rotate(45deg); } .sweet-alert .icon.success .line.long { width: 47px; right: 8px; top: 38px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); } .sweet-alert .icon.custom { background-size: contain; border-radius: 0; border: none; background-position: center center; background-repeat: no-repeat; } .sweet-alert .btn-default:focus { border-color: #cccccc; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(204, 204, 204, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(204, 204, 204, 0.6); } .sweet-alert .btn-success:focus { border-color: #4cae4c; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6); } .sweet-alert .btn-info:focus { border-color: #46b8da; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6); } .sweet-alert .btn-danger:focus { border-color: #d43f3a; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6); } .sweet-alert .btn-warning:focus { border-color: #eea236; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6); } .sweet-alert button::-moz-focus-inner { border: 0; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-sweetalert/lib/sweet-alert.js ================================================ // SweetAlert // 2014 (c) - Tristan Edwards // github.com/t4t5/sweetalert (function(window, document) { var modalClass = '.sweet-alert', overlayClass = '.sweet-overlay', alertTypes = ['error', 'warning', 'info', 'success'], defaultParams = { title: '', text: '', type: null, allowOutsideClick: false, showCancelButton: false, showConfirmButton: true, closeOnConfirm: true, closeOnCancel: true, confirmButtonText: 'OK', confirmButtonClass: 'btn-primary', cancelButtonText: 'Cancel', cancelButtonClass: 'btn-default', containerClass: '', titleClass: '', textClass: '', imageUrl: null, imageSize: null, timer: null }; /* * Manipulate DOM */ var getModal = function() { return document.querySelector(modalClass); }, getOverlay = function() { return document.querySelector(overlayClass); }, hasClass = function(elem, className) { return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' '); }, addClass = function(elem, className) { if (className && !hasClass(elem, className)) { elem.className += ' ' + className; } }, removeClass = function(elem, className) { var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' '; if (hasClass(elem, className)) { while (newClass.indexOf(' ' + className + ' ') >= 0) { newClass = newClass.replace(' ' + className + ' ', ' '); } elem.className = newClass.replace(/^\s+|\s+$/g, ''); } }, escapeHtml = function(str) { var div = document.createElement('div'); div.appendChild(document.createTextNode(str)); return div.innerHTML; }, _show = function(elem) { elem.style.opacity = ''; elem.style.display = 'block'; }, show = function(elems) { if (elems && !elems.length) { return _show(elems); } for (var i = 0; i < elems.length; ++i) { _show(elems[i]); } }, _hide = function(elem) { elem.style.opacity = ''; elem.style.display = 'none'; }, hide = function(elems) { if (elems && !elems.length) { return _hide(elems); } for (var i = 0; i < elems.length; ++i) { _hide(elems[i]); } }, isDescendant = function(parent, child) { var node = child.parentNode; while (node !== null) { if (node === parent) { return true; } node = node.parentNode; } return false; }, getTopMargin = function(elem) { elem.style.left = '-9999px'; elem.style.display = 'block'; var height = elem.clientHeight; var padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10); elem.style.left = ''; elem.style.display = 'none'; return ('-' + parseInt(height / 2 + padding) + 'px'); }, fadeIn = function(elem, interval) { if(+elem.style.opacity < 1) { interval = interval || 16; elem.style.opacity = 0; elem.style.display = 'block'; var last = +new Date(); var tick = function() { elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100; last = +new Date(); if (+elem.style.opacity < 1) { setTimeout(tick, interval); } }; tick(); } }, fadeOut = function(elem, interval) { interval = interval || 16; elem.style.opacity = 1; var last = +new Date(); var tick = function() { elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100; last = +new Date(); if (+elem.style.opacity > 0) { setTimeout(tick, interval); } else { elem.style.display = 'none'; } }; tick(); }, fireClick = function(node) { // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/ // Then fixed for today's Chrome browser. if (MouseEvent) { // Up-to-date approach var mevt = new MouseEvent('click', { view: window, bubbles: false, cancelable: true }); node.dispatchEvent(mevt); } else if ( document.createEvent ) { // Fallback var evt = document.createEvent('MouseEvents'); evt.initEvent('click', false, false); node.dispatchEvent(evt); } else if( document.createEventObject ) { node.fireEvent('onclick') ; } else if (typeof node.onclick === 'function' ) { node.onclick(); } }, stopEventPropagation = function(e) { // In particular, make sure the space bar doesn't scroll the main window. if (typeof e.stopPropagation === 'function') { e.stopPropagation(); e.preventDefault(); } else if (window.event && window.event.hasOwnProperty('cancelBubble')) { window.event.cancelBubble = true; } }; // Remember state in cases where opening and handling a modal will fiddle with it. var previousActiveElement, previousDocumentClick, previousWindowKeyDown, lastFocusedButton; /* * Add modal + overlay to DOM */ window.sweetAlertInitialize = function() { var sweetHTML = '

                                  Title

                                  Text

                                  ', sweetWrap = document.createElement('div'); sweetWrap.innerHTML = sweetHTML; // For readability: check sweet-alert.html document.body.appendChild(sweetWrap); // For development use only! /*jQuery.ajax({ url: '../lib/sweet-alert.html', // Change path depending on file location dataType: 'html' }) .done(function(html) { jQuery('body').append(html); });*/ } /* * Global sweetAlert function */ window.sweetAlert = window.swal = function() { if (arguments[0] === undefined) { window.console.error('sweetAlert expects at least 1 attribute!'); return false; } var params = extend({}, defaultParams); switch (typeof arguments[0]) { case 'string': params.title = arguments[0]; params.text = arguments[1] || ''; params.type = arguments[2] || ''; break; case 'object': if (arguments[0].title === undefined) { window.console.error('Missing "title" argument!'); return false; } params.title = arguments[0].title; params.text = arguments[0].text || defaultParams.text; params.type = arguments[0].type || defaultParams.type; params.allowOutsideClick = arguments[0].allowOutsideClick || defaultParams.allowOutsideClick; params.showCancelButton = arguments[0].showCancelButton !== undefined ? arguments[0].showCancelButton : defaultParams.showCancelButton; params.showConfirmButton = arguments[0].showConfirmButton !== undefined ? arguments[0].showConfirmButton : defaultParams.showConfirmButton; params.closeOnConfirm = arguments[0].closeOnConfirm !== undefined ? arguments[0].closeOnConfirm : defaultParams.closeOnConfirm; params.closeOnCancel = arguments[0].closeOnCancel !== undefined ? arguments[0].closeOnCancel : defaultParams.closeOnCancel; params.timer = arguments[0].timer || defaultParams.timer; // Show "Confirm" instead of "OK" if cancel button is visible params.confirmButtonText = (defaultParams.showCancelButton) ? 'Confirm' : defaultParams.confirmButtonText; params.confirmButtonText = arguments[0].confirmButtonText || defaultParams.confirmButtonText; params.confirmButtonClass = arguments[0].confirmButtonClass || (arguments[0].type ? 'btn-' + arguments[0].type : null) || defaultParams.confirmButtonClass; params.cancelButtonText = arguments[0].cancelButtonText || defaultParams.cancelButtonText; params.cancelButtonClass = arguments[0].cancelButtonClass || defaultParams.cancelButtonClass; params.containerClass = arguments[0].containerClass || defaultParams.containerClass; params.titleClass = arguments[0].titleClass || defaultParams.titleClass; params.textClass = arguments[0].textClass || defaultParams.textClass; params.imageUrl = arguments[0].imageUrl || defaultParams.imageUrl; params.imageSize = arguments[0].imageSize || defaultParams.imageSize; params.doneFunction = arguments[1] || null; break; default: window.console.error('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]); return false; } setParameters(params); fixVerticalPosition(); openModal(); // Modal interactions var modal = getModal(); // Mouse interactions var onButtonEvent = function(e) { var target = e.target || e.srcElement, targetedConfirm = (target.className.indexOf('confirm') > -1), modalIsVisible = hasClass(modal, 'visible'), doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true'); switch (e.type) { case ("click"): if (targetedConfirm && doneFunctionExists && modalIsVisible) { // Clicked "confirm" params.doneFunction(true); if (params.closeOnConfirm) { closeModal(); } } else if (doneFunctionExists && modalIsVisible) { // Clicked "cancel" // Check if callback function expects a parameter (to track cancel actions) var functionAsStr = String(params.doneFunction).replace(/\s/g, ''); var functionHandlesCancel = functionAsStr.substring(0, 9) === "function(" && functionAsStr.substring(9, 10) !== ")"; if (functionHandlesCancel) { params.doneFunction(false); } if (params.closeOnCancel) { closeModal(); } } else { closeModal(); } break; } }; var $buttons = modal.querySelectorAll('button'); for (var i = 0; i < $buttons.length; i++) { $buttons[i].onclick = onButtonEvent; } // Remember the current document.onclick event. previousDocumentClick = document.onclick; document.onclick = function(e) { var target = e.target || e.srcElement; var clickedOnModal = (modal === target), clickedOnModalChild = isDescendant(modal, e.target), modalIsVisible = hasClass(modal, 'visible'), outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true'; if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) { closeModal(); } }; // Keyboard interactions var $okButton = modal.querySelector('button.confirm'), $cancelButton = modal.querySelector('button.cancel'), $modalButtons = modal.querySelectorAll('button:not([type=hidden])'); function handleKeyDown(e) { var keyCode = e.keyCode || e.which; if ([9,13,32,27].indexOf(keyCode) === -1) { // Don't do work on keys we don't care about. return; } var $targetElement = e.target || e.srcElement; var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. for (var i = 0; i < $modalButtons.length; i++) { if ($targetElement === $modalButtons[i]) { btnIndex = i; break; } } if (keyCode === 9) { // TAB if (btnIndex === -1) { // No button focused. Jump to the confirm button. $targetElement = $okButton; } else { // Cycle to the next button if (btnIndex === $modalButtons.length - 1) { $targetElement = $modalButtons[0]; } else { $targetElement = $modalButtons[btnIndex + 1]; } } stopEventPropagation(e); $targetElement.focus(); } else { if (keyCode === 13 || keyCode === 32) { if (btnIndex === -1) { // ENTER/SPACE clicked outside of a button. $targetElement = $okButton; } else { // Do nothing - let the browser handle it. $targetElement = undefined; } } else if (keyCode === 27 && !($cancelButton.hidden || $cancelButton.style.display === 'none')) { // ESC to cancel only if there's a cancel button displayed (like the alert() window). $targetElement = $cancelButton; } else { // Fallback - let the browser handle it. $targetElement = undefined; } if ($targetElement !== undefined) { fireClick($targetElement, e); } } } previousWindowKeyDown = window.onkeydown; window.onkeydown = handleKeyDown; function handleOnBlur(e) { var $targetElement = e.target || e.srcElement, $focusElement = e.relatedTarget, modalIsVisible = hasClass(modal, 'visible'); if (modalIsVisible) { var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. if ($focusElement !== null) { // If we picked something in the DOM to focus to, let's see if it was a button. for (var i = 0; i < $modalButtons.length; i++) { if ($focusElement === $modalButtons[i]) { btnIndex = i; break; } } if (btnIndex === -1) { // Something in the dom, but not a visible button. Focus back on the button. $targetElement.focus(); } } else { // Exiting the DOM (e.g. clicked in the URL bar); lastFocusedButton = $targetElement; } } } $okButton.onblur = handleOnBlur; $cancelButton.onblur = handleOnBlur; window.onfocus = function() { // When the user has focused away and focused back from the whole window. window.setTimeout(function() { // Put in a timeout to jump out of the event sequence. Calling focus() in the event // sequence confuses things. if (lastFocusedButton !== undefined) { lastFocusedButton.focus(); lastFocusedButton = undefined; } }, 0); }; }; /** * Set default params for each popup * @param {Object} userParams */ window.swal.setDefaults = function(userParams) { if (!userParams) { throw new Error('userParams is required'); } if (typeof userParams !== 'object') { throw new Error('userParams has to be a object'); } extend(defaultParams, userParams); }; /** * Closes the current modal */ window.swal.close = function() { closeModal(); } /* * Set type, text and actions on modal */ function setParameters(params) { var modal = getModal(); var $title = modal.querySelector('h2'), $text = modal.querySelector('p'), $cancelBtn = modal.querySelector('button.cancel'), $confirmBtn = modal.querySelector('button.confirm'); // Title $title.innerHTML = escapeHtml(params.title).split("\n").join("
                                  "); // Text $text.innerHTML = escapeHtml(params.text || '').split("\n").join("
                                  "); if (params.text) { show($text); } // Icon hide(modal.querySelectorAll('.icon')); if (params.type) { var validType = false; for (var i = 0; i < alertTypes.length; i++) { if (params.type === alertTypes[i]) { validType = true; break; } } if (!validType) { window.console.error('Unknown alert type: ' + params.type); return false; } var $icon = modal.querySelector('.icon.' + params.type); show($icon); // Animate icon switch (params.type) { case "success": addClass($icon, 'animate'); addClass($icon.querySelector('.tip'), 'animateSuccessTip'); addClass($icon.querySelector('.long'), 'animateSuccessLong'); break; case "error": addClass($icon, 'animateErrorIcon'); addClass($icon.querySelector('.x-mark'), 'animateXMark'); break; case "warning": addClass($icon, 'pulseWarning'); addClass($icon.querySelector('.body'), 'pulseWarningIns'); addClass($icon.querySelector('.dot'), 'pulseWarningIns'); break; } } // Custom image if (params.imageUrl) { var $customIcon = modal.querySelector('.icon.custom'); $customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')'; show($customIcon); var _imgWidth = 80, _imgHeight = 80; if (params.imageSize) { var imgWidth = params.imageSize.split('x')[0]; var imgHeight = params.imageSize.split('x')[1]; if (!imgWidth || !imgHeight) { window.console.error("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize); } else { _imgWidth = imgWidth; _imgHeight = imgHeight; $customIcon.css({ 'width': imgWidth + 'px', 'height': imgHeight + 'px' }); } } $customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px'); } // Cancel button modal.setAttribute('data-has-cancel-button', params.showCancelButton); if (params.showCancelButton) { $cancelBtn.style.display = 'inline-block'; } else { hide($cancelBtn); } //Confirm button modal.setAttribute('data-has-confirm-button', params.showConfirmButton); if (params.showConfirmButton) { $confirmBtn.style.display = 'inline-block'; } else { hide($confirmBtn); } // Edit text on cancel and confirm buttons if (params.cancelButtonText) { $cancelBtn.innerHTML = escapeHtml(params.cancelButtonText); } if (params.confirmButtonText) { $confirmBtn.innerHTML = escapeHtml(params.confirmButtonText); } // Reset confirm buttons to default class (Ugly fix) $confirmBtn.className = 'confirm btn btn-lg'; // Attach selected class to the sweet alert modal addClass(modal, params.containerClass); // Set confirm button to selected class addClass($confirmBtn, params.confirmButtonClass); // Set cancel button to selected class addClass($cancelBtn, params.cancelButtonClass); // Set title to selected class addClass($title, params.titleClass); // Set text to selected class addClass($text, params.textClass); // Allow outside click? modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick); // Done-function var hasDoneFunction = (params.doneFunction) ? true : false; modal.setAttribute('data-has-done-function', hasDoneFunction); // Close timer modal.setAttribute('data-timer', params.timer); } /* * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color) */ function colorLuminance(hex, lum) { // Validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, ''); if (hex.length < 6) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; } lum = lum || 0; // Convert to decimal and change luminosity var rgb = "#", c, i; for (i = 0; i < 3; i++) { c = parseInt(hex.substr(i*2,2), 16); c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); rgb += ("00"+c).substr(c.length); } return rgb; } function extend(a, b){ for (var key in b) { if (b.hasOwnProperty(key)) { a[key] = b[key]; } } return a; } function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null; } // Add box-shadow style to button (depending on its chosen bg-color) function setFocusStyle($button, bgColor) { var rgbColor = hexToRgb(bgColor); $button.style.boxShadow = '0 0 2px rgba(' + rgbColor +', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)'; } /* * Animations */ function openModal() { var modal = getModal(); fadeIn(getOverlay(), 10); show(modal); addClass(modal, 'showSweetAlert'); removeClass(modal, 'hideSweetAlert'); previousActiveElement = document.activeElement; var $okButton = modal.querySelector('button.confirm'); $okButton.focus(); setTimeout(function() { addClass(modal, 'visible'); }, 500); var timer = modal.getAttribute('data-timer'); if (timer !== "null" && timer !== "") { setTimeout(function() { closeModal(); }, timer); } } function closeModal() { var modal = getModal(); fadeOut(getOverlay(), 5); fadeOut(modal, 5); removeClass(modal, 'showSweetAlert'); addClass(modal, 'hideSweetAlert'); removeClass(modal, 'visible'); // Reset icon animations var $successIcon = modal.querySelector('.icon.success'); removeClass($successIcon, 'animate'); removeClass($successIcon.querySelector('.tip'), 'animateSuccessTip'); removeClass($successIcon.querySelector('.long'), 'animateSuccessLong'); var $errorIcon = modal.querySelector('.icon.error'); removeClass($errorIcon, 'animateErrorIcon'); removeClass($errorIcon.querySelector('.x-mark'), 'animateXMark'); var $warningIcon = modal.querySelector('.icon.warning'); removeClass($warningIcon, 'pulseWarning'); removeClass($warningIcon.querySelector('.body'), 'pulseWarningIns'); removeClass($warningIcon.querySelector('.dot'), 'pulseWarningIns'); // Reset the page to its previous state window.onkeydown = previousWindowKeyDown; document.onclick = previousDocumentClick; if (previousActiveElement) { previousActiveElement.focus(); } lastFocusedButton = undefined; } /* * Set "margin-top"-property on modal based on its computed height */ function fixVerticalPosition() { var modal = getModal(); modal.style.marginTop = getTopMargin(getModal()); } /* * If library is injected after page has loaded */ (function () { if (document.readyState === "complete" || document.readyState === "interactive" && document.body) { sweetAlertInitialize(); } else { if (document.addEventListener) { document.addEventListener('DOMContentLoaded', function handler() { document.removeEventListener('DOMContentLoaded', handler, false); sweetAlertInitialize(); }, false); } else if (document.attachEvent) { document.attachEvent('onreadystatechange', function handler() { if (document.readyState === 'complete') { document.detachEvent('onreadystatechange', handler); sweetAlertInitialize(); } }); } } })(); })(window, document); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/bootstrap-sweetalert/lib/sweet-alert.less ================================================ // SweetAlert // 2014 (c) - Tristan Edwards // github.com/t4t5/sweetalert @import "sweet-alert-animations"; .sweet-overlay { background-color: fade(#000, 40%); position: fixed; left: 0; right: 0; top: 0; bottom: 0; display: none; z-index: @zindex-modal; } .sweet-alert { @width: 478px; @padding: 17px; background-color: @body-bg; width: @width; padding: @padding; border-radius: 5px; text-align: center; position: fixed; left: 50%; top: 50%; margin-left: -(@width / 2 + @padding); margin-top: -200px; overflow: hidden; display: none; z-index: 2000; @media all and (max-width: @screen-xs-max) { width: auto; margin-left: 0; margin-right: 0; left: (@grid-gutter-width / 2); right: (@grid-gutter-width / 2); } .icon { width: 80px; height: 80px; border: 4px solid gray; border-radius: 50%; margin: 20px auto; position: relative; box-sizing: content-box; &.error { border-color: @btn-danger-border; .x-mark { position: relative; display: block; } .line { position: absolute; height: 5px; width: 47px; background-color: @btn-danger-bg; display: block; top: 37px; border-radius: 2px; &.left { -webkit-transform: rotate(45deg); transform: rotate(45deg); left: 17px; } &.right { -webkit-transform: rotate(-45deg); transform: rotate(-45deg); right: 16px; } } } &.warning { border-color: @btn-warning-border; .body { // Exclamation mark body position: absolute; width: 5px; height: 47px; left: 50%; top: 10px; border-radius: 2px; margin-left: -2px; background-color: @btn-warning-bg; } .dot { // Exclamation mark dot position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -3px; left: 50%; bottom: 10px; background-color: @btn-warning-bg; } } &.info { border-color: @btn-info-border; &::before { // i-letter body content: ""; position: absolute; width: 5px; height: 29px; left: 50%; bottom: 17px; border-radius: 2px; margin-left: -2px; background-color: @btn-info-bg; } &::after { // i-letter dot content: ""; position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -3px; top: 19px; background-color: @btn-info-bg; } } &.success { border-color: @btn-success-border; &::before, &::after { // Emulate moving circular line content: ''; border-radius: 50%; position: absolute; width: 60px; height: 120px; background: white; -webkit-transform: rotate(45deg); transform: rotate(45deg); } &::before { border-radius: 120px 0 0 120px; top: -7px; left: -33px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: 60px 60px; transform-origin: 60px 60px; } &::after { border-radius: 0 120px 120px 0; top: -11px; left: 30px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: 0px 60px; transform-origin: 0px 60px; } .placeholder { // Ring width: 80px; height: 80px; border: 4px solid fade(@brand-success, 20%); border-radius: 50%; box-sizing: content-box; position: absolute; left: -4px; top: -4px; z-index: 2; } .fix { // Hide corners left from animation width: 5px; height: 90px; background-color: @body-bg; position: absolute; left: 28px; top: 8px; z-index: 1; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); } .line { height: 5px; background-color: @btn-success-bg; display: block; border-radius: 2px; position: absolute; z-index: 2; &.tip { width: 25px; left: 14px; top: 46px; -webkit-transform: rotate(45deg); transform: rotate(45deg); } &.long { width: 47px; right: 8px; top: 38px; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); } } } &.custom { background-size: contain; border-radius: 0; border: none; background-position: center center; background-repeat: no-repeat; } } .btn-default { .form-control-focus(@btn-default-border); } .btn-success { .form-control-focus(@btn-success-border); } .btn-info { .form-control-focus(@btn-info-border); } .btn-danger { .form-control-focus(@btn-danger-border); } .btn-warning { .form-control-focus(@btn-warning-border); } button::-moz-focus-inner { border: 0; } } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/eonasdan-bootstrap-datetimepicker/4.7.14/css/bootstrap-datetimepicker.css ================================================ /*! * Datetimepicker for Bootstrap 3 * ! version : 4.7.14 * https://github.com/Eonasdan/bootstrap-datetimepicker/ */ .bootstrap-datetimepicker-widget { list-style: none; } .bootstrap-datetimepicker-widget.dropdown-menu { margin: 2px 0; padding: 4px; width: 19em; } @media (min-width: 768px) { .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { width: 38em; } } @media (min-width: 992px) { .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { width: 38em; } } @media (min-width: 1200px) { .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { width: 38em; } } .bootstrap-datetimepicker-widget.dropdown-menu:before, .bootstrap-datetimepicker-widget.dropdown-menu:after { content: ''; display: inline-block; position: absolute; } .bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 7px solid #cccccc; border-bottom-color: rgba(0, 0, 0, 0.2); top: -7px; left: 7px; } .bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 6px solid white; top: -6px; left: 8px; } .bootstrap-datetimepicker-widget.dropdown-menu.top:before { border-left: 7px solid transparent; border-right: 7px solid transparent; border-top: 7px solid #cccccc; border-top-color: rgba(0, 0, 0, 0.2); bottom: -7px; left: 6px; } .bootstrap-datetimepicker-widget.dropdown-menu.top:after { border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 6px solid white; bottom: -6px; left: 7px; } .bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { left: auto; right: 6px; } .bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { left: auto; right: 7px; } .bootstrap-datetimepicker-widget .list-unstyled { margin: 0; } .bootstrap-datetimepicker-widget a[data-action] { padding: 6px 0; } .bootstrap-datetimepicker-widget a[data-action]:active { box-shadow: none; } .bootstrap-datetimepicker-widget .timepicker-hour, .bootstrap-datetimepicker-widget .timepicker-minute, .bootstrap-datetimepicker-widget .timepicker-second { width: 54px; font-weight: bold; font-size: 1.2em; margin: 0; } .bootstrap-datetimepicker-widget button[data-action] { padding: 6px; } .bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Increment Hours"; } .bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Increment Minutes"; } .bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Decrement Hours"; } .bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Decrement Minutes"; } .bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Show Hours"; } .bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Show Minutes"; } .bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Toggle AM/PM"; } .bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Clear the picker"; } .bootstrap-datetimepicker-widget .btn[data-action="today"]::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Set the date to today"; } .bootstrap-datetimepicker-widget .picker-switch { text-align: center; } .bootstrap-datetimepicker-widget .picker-switch::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Toggle Date and Time Screens"; } .bootstrap-datetimepicker-widget .picker-switch td { padding: 0; margin: 0; height: auto; width: auto; line-height: inherit; } .bootstrap-datetimepicker-widget .picker-switch td span { line-height: 2.5; height: 2.5em; width: 100%; } .bootstrap-datetimepicker-widget table { width: 100%; margin: 0; } .bootstrap-datetimepicker-widget table td, .bootstrap-datetimepicker-widget table th { text-align: center; border-radius: 4px; } .bootstrap-datetimepicker-widget table th { height: 20px; line-height: 20px; width: 20px; } .bootstrap-datetimepicker-widget table th.picker-switch { width: 145px; } .bootstrap-datetimepicker-widget table th.disabled, .bootstrap-datetimepicker-widget table th.disabled:hover { background: none; color: #777777; cursor: not-allowed; } .bootstrap-datetimepicker-widget table th.prev::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Previous Month"; } .bootstrap-datetimepicker-widget table th.next::after { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; content: "Next Month"; } .bootstrap-datetimepicker-widget table thead tr:first-child th { cursor: pointer; } .bootstrap-datetimepicker-widget table thead tr:first-child th:hover { background: #eeeeee; } .bootstrap-datetimepicker-widget table td { height: 54px; line-height: 54px; width: 54px; } .bootstrap-datetimepicker-widget table td.cw { font-size: .8em; height: 20px; line-height: 20px; color: #777777; } .bootstrap-datetimepicker-widget table td.day { height: 20px; line-height: 20px; width: 20px; } .bootstrap-datetimepicker-widget table td.day:hover, .bootstrap-datetimepicker-widget table td.hour:hover, .bootstrap-datetimepicker-widget table td.minute:hover, .bootstrap-datetimepicker-widget table td.second:hover { background: #eeeeee; cursor: pointer; } .bootstrap-datetimepicker-widget table td.old, .bootstrap-datetimepicker-widget table td.new { color: #777777; } .bootstrap-datetimepicker-widget table td.today { position: relative; } .bootstrap-datetimepicker-widget table td.today:before { content: ''; display: inline-block; border: 0 0 7px 7px solid transparent; border-bottom-color: #337ab7; border-top-color: rgba(0, 0, 0, 0.2); position: absolute; bottom: 4px; right: 4px; } .bootstrap-datetimepicker-widget table td.active, .bootstrap-datetimepicker-widget table td.active:hover { background-color: #337ab7; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .bootstrap-datetimepicker-widget table td.active.today:before { border-bottom-color: #fff; } .bootstrap-datetimepicker-widget table td.disabled, .bootstrap-datetimepicker-widget table td.disabled:hover { background: none; color: #777777; cursor: not-allowed; } .bootstrap-datetimepicker-widget table td span { display: inline-block; width: 54px; height: 54px; line-height: 54px; margin: 2px 1.5px; cursor: pointer; border-radius: 4px; } .bootstrap-datetimepicker-widget table td span:hover { background: #eeeeee; } .bootstrap-datetimepicker-widget table td span.active { background-color: #337ab7; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .bootstrap-datetimepicker-widget table td span.old { color: #777777; } .bootstrap-datetimepicker-widget table td span.disabled, .bootstrap-datetimepicker-widget table td span.disabled:hover { background: none; color: #777777; cursor: not-allowed; } .bootstrap-datetimepicker-widget.usetwentyfour td.hour { height: 27px; line-height: 27px; } .input-group.date .input-group-addon { cursor: pointer; } .sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot/jquery.flot.js ================================================ /* Javascript plotting library for jQuery, version 0.8.3. Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. */ // first an inline dependency, jquery.colorhelpers.js, we inline it here // for convenience /* Plugin for jQuery for working with colors. * * Version 1.1. * * Inspiration from jQuery color animation plugin by John Resig. * * Released under the MIT license by Ole Laursen, October 2009. * * Examples: * * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() * var c = $.color.extract($("#mydiv"), 'background-color'); * console.log(c.r, c.g, c.b, c.a); * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" * * Note that .scale() and .add() return the same modified object * instead of making a new one. * * V. 1.1: Fix error handling so e.g. parsing an empty string does * produce a color rather than just crashing. */ (function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); // the actual Flot code (function($) { // Cache the prototype hasOwnProperty for faster access var hasOwnProperty = Object.prototype.hasOwnProperty; // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM // operation produces the same effect as detach, i.e. removing the element // without touching its jQuery data. // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. if (!$.fn.detach) { $.fn.detach = function() { return this.each(function() { if (this.parentNode) { this.parentNode.removeChild( this ); } }); }; } /////////////////////////////////////////////////////////////////////////// // The Canvas object is a wrapper around an HTML5 tag. // // @constructor // @param {string} cls List of classes to apply to the canvas. // @param {element} container Element onto which to append the canvas. // // Requiring a container is a little iffy, but unfortunately canvas // operations don't work unless the canvas is attached to the DOM. function Canvas(cls, container) { var element = container.children("." + cls)[0]; if (element == null) { element = document.createElement("canvas"); element.className = cls; $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) .appendTo(container); // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas if (!element.getContext) { if (window.G_vmlCanvasManager) { element = window.G_vmlCanvasManager.initElement(element); } else { throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); } } } this.element = element; var context = this.context = element.getContext("2d"); // Determine the screen's ratio of physical to device-independent // pixels. This is the ratio between the canvas width that the browser // advertises and the number of pixels actually present in that space. // The iPhone 4, for example, has a device-independent width of 320px, // but its screen is actually 640px wide. It therefore has a pixel // ratio of 2, while most normal devices have a ratio of 1. var devicePixelRatio = window.devicePixelRatio || 1, backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; this.pixelRatio = devicePixelRatio / backingStoreRatio; // Size the canvas to match the internal dimensions of its container this.resize(container.width(), container.height()); // Collection of HTML div layers for text overlaid onto the canvas this.textContainer = null; this.text = {}; // Cache of text fragments and metrics, so we can avoid expensively // re-calculating them when the plot is re-rendered in a loop. this._textCache = {}; } // Resizes the canvas to the given dimensions. // // @param {number} width New width of the canvas, in pixels. // @param {number} width New height of the canvas, in pixels. Canvas.prototype.resize = function(width, height) { if (width <= 0 || height <= 0) { throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); } var element = this.element, context = this.context, pixelRatio = this.pixelRatio; // Resize the canvas, increasing its density based on the display's // pixel ratio; basically giving it more pixels without increasing the // size of its element, to take advantage of the fact that retina // displays have that many more pixels in the same advertised space. // Resizing should reset the state (excanvas seems to be buggy though) if (this.width != width) { element.width = width * pixelRatio; element.style.width = width + "px"; this.width = width; } if (this.height != height) { element.height = height * pixelRatio; element.style.height = height + "px"; this.height = height; } // Save the context, so we can reset in case we get replotted. The // restore ensure that we're really back at the initial state, and // should be safe even if we haven't saved the initial state yet. context.restore(); context.save(); // Scale the coordinate space to match the display density; so even though we // may have twice as many pixels, we still want lines and other drawing to // appear at the same size; the extra pixels will just make them crisper. context.scale(pixelRatio, pixelRatio); }; // Clears the entire canvas area, not including any overlaid HTML text Canvas.prototype.clear = function() { this.context.clearRect(0, 0, this.width, this.height); }; // Finishes rendering the canvas, including managing the text overlay. Canvas.prototype.render = function() { var cache = this._textCache; // For each text layer, add elements marked as active that haven't // already been rendered, and remove those that are no longer active. for (var layerKey in cache) { if (hasOwnProperty.call(cache, layerKey)) { var layer = this.getTextLayer(layerKey), layerCache = cache[layerKey]; layer.hide(); for (var styleKey in layerCache) { if (hasOwnProperty.call(layerCache, styleKey)) { var styleCache = layerCache[styleKey]; for (var key in styleCache) { if (hasOwnProperty.call(styleCache, key)) { var positions = styleCache[key].positions; for (var i = 0, position; position = positions[i]; i++) { if (position.active) { if (!position.rendered) { layer.append(position.element); position.rendered = true; } } else { positions.splice(i--, 1); if (position.rendered) { position.element.detach(); } } } if (positions.length == 0) { delete styleCache[key]; } } } } } layer.show(); } } }; // Creates (if necessary) and returns the text overlay container. // // @param {string} classes String of space-separated CSS classes used to // uniquely identify the text layer. // @return {object} The jQuery-wrapped text-layer div. Canvas.prototype.getTextLayer = function(classes) { var layer = this.text[classes]; // Create the text layer if it doesn't exist if (layer == null) { // Create the text layer container, if it doesn't exist if (this.textContainer == null) { this.textContainer = $("
                                  ") .css({ position: "absolute", top: 0, left: 0, bottom: 0, right: 0, 'font-size': "smaller", color: "#545454" }) .insertAfter(this.element); } layer = this.text[classes] = $("
                                  ") .addClass(classes) .css({ position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }) .appendTo(this.textContainer); } return layer; }; // Creates (if necessary) and returns a text info object. // // The object looks like this: // // { // width: Width of the text's wrapper div. // height: Height of the text's wrapper div. // element: The jQuery-wrapped HTML div containing the text. // positions: Array of positions at which this text is drawn. // } // // The positions array contains objects that look like this: // // { // active: Flag indicating whether the text should be visible. // rendered: Flag indicating whether the text is currently visible. // element: The jQuery-wrapped HTML div containing the text. // x: X coordinate at which to draw the text. // y: Y coordinate at which to draw the text. // } // // Each position after the first receives a clone of the original element. // // The idea is that that the width, height, and general 'identity' of the // text is constant no matter where it is placed; the placements are a // secondary property. // // Canvas maintains a cache of recently-used text info objects; getTextInfo // either returns the cached element or creates a new entry. // // @param {string} layer A string of space-separated CSS classes uniquely // identifying the layer containing this text. // @param {string} text Text string to retrieve info for. // @param {(string|object)=} font Either a string of space-separated CSS // classes or a font-spec object, defining the text's font and style. // @param {number=} angle Angle at which to rotate the text, in degrees. // Angle is currently unused, it will be implemented in the future. // @param {number=} width Maximum width of the text before it wraps. // @return {object} a text info object. Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { var textStyle, layerCache, styleCache, info; // Cast the value to a string, in case we were given a number or such text = "" + text; // If the font is a font-spec object, generate a CSS font definition if (typeof font === "object") { textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; } else { textStyle = font; } // Retrieve (or create) the cache for the text's layer and styles layerCache = this._textCache[layer]; if (layerCache == null) { layerCache = this._textCache[layer] = {}; } styleCache = layerCache[textStyle]; if (styleCache == null) { styleCache = layerCache[textStyle] = {}; } info = styleCache[text]; // If we can't find a matching element in our cache, create a new one if (info == null) { var element = $("
                                  ").html(text) .css({ position: "absolute", 'max-width': width, top: -9999 }) .appendTo(this.getTextLayer(layer)); if (typeof font === "object") { element.css({ font: textStyle, color: font.color }); } else if (typeof font === "string") { element.addClass(font); } info = styleCache[text] = { width: element.outerWidth(true), height: element.outerHeight(true), element: element, positions: [] }; element.detach(); } return info; }; // Adds a text string to the canvas text overlay. // // The text isn't drawn immediately; it is marked as rendering, which will // result in its addition to the canvas on the next render pass. // // @param {string} layer A string of space-separated CSS classes uniquely // identifying the layer containing this text. // @param {number} x X coordinate at which to draw the text. // @param {number} y Y coordinate at which to draw the text. // @param {string} text Text string to draw. // @param {(string|object)=} font Either a string of space-separated CSS // classes or a font-spec object, defining the text's font and style. // @param {number=} angle Angle at which to rotate the text, in degrees. // Angle is currently unused, it will be implemented in the future. // @param {number=} width Maximum width of the text before it wraps. // @param {string=} halign Horizontal alignment of the text; either "left", // "center" or "right". // @param {string=} valign Vertical alignment of the text; either "top", // "middle" or "bottom". Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { var info = this.getTextInfo(layer, text, font, angle, width), positions = info.positions; // Tweak the div's position to match the text's alignment if (halign == "center") { x -= info.width / 2; } else if (halign == "right") { x -= info.width; } if (valign == "middle") { y -= info.height / 2; } else if (valign == "bottom") { y -= info.height; } // Determine whether this text already exists at this position. // If so, mark it for inclusion in the next render pass. for (var i = 0, position; position = positions[i]; i++) { if (position.x == x && position.y == y) { position.active = true; return; } } // If the text doesn't exist at this position, create a new entry // For the very first position we'll re-use the original element, // while for subsequent ones we'll clone it. position = { active: true, rendered: false, element: positions.length ? info.element.clone() : info.element, x: x, y: y }; positions.push(position); // Move the element to its final position within the container position.element.css({ top: Math.round(y), left: Math.round(x), 'text-align': halign // In case the text wraps }); }; // Removes one or more text strings from the canvas text overlay. // // If no parameters are given, all text within the layer is removed. // // Note that the text is not immediately removed; it is simply marked as // inactive, which will result in its removal on the next render pass. // This avoids the performance penalty for 'clear and redraw' behavior, // where we potentially get rid of all text on a layer, but will likely // add back most or all of it later, as when redrawing axes, for example. // // @param {string} layer A string of space-separated CSS classes uniquely // identifying the layer containing this text. // @param {number=} x X coordinate of the text. // @param {number=} y Y coordinate of the text. // @param {string=} text Text string to remove. // @param {(string|object)=} font Either a string of space-separated CSS // classes or a font-spec object, defining the text's font and style. // @param {number=} angle Angle at which the text is rotated, in degrees. // Angle is currently unused, it will be implemented in the future. Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { if (text == null) { var layerCache = this._textCache[layer]; if (layerCache != null) { for (var styleKey in layerCache) { if (hasOwnProperty.call(layerCache, styleKey)) { var styleCache = layerCache[styleKey]; for (var key in styleCache) { if (hasOwnProperty.call(styleCache, key)) { var positions = styleCache[key].positions; for (var i = 0, position; position = positions[i]; i++) { position.active = false; } } } } } } } else { var positions = this.getTextInfo(layer, text, font, angle).positions; for (var i = 0, position; position = positions[i]; i++) { if (position.x == x && position.y == y) { position.active = false; } } } }; /////////////////////////////////////////////////////////////////////////// // The top-level container for the entire plot. function Plot(placeholder, data_, options_, plugins) { // data is on the form: // [ series1, series2 ... ] // where series is either just the data as [ [x1, y1], [x2, y2], ... ] // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } var series = [], options = { // the color theme used for graphs colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], legend: { show: true, noColumns: 1, // number of colums in legend table labelFormatter: null, // fn: string -> string labelBoxBorderColor: "#ccc", // border color for the little label boxes container: null, // container (as jQuery object) to put legend in, null means default on top of graph position: "ne", // position of default legend container within plot margin: 5, // distance from grid edge to default legend container within plot backgroundColor: null, // null means auto-detect backgroundOpacity: 0.85, // set to 0 to avoid background sorted: null // default to no legend sorting }, xaxis: { show: null, // null = auto-detect, true = always, false = never position: "bottom", // or "top" mode: null, // null or "time" font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } color: null, // base color, labels, ticks tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" transform: null, // null or f: number -> number to transform axis inverseTransform: null, // if transform is set, this should be the inverse function min: null, // min. value to show, null means set automatically max: null, // max. value to show, null means set automatically autoscaleMargin: null, // margin in % to add if auto-setting min/max ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks tickFormatter: null, // fn: number -> string labelWidth: null, // size of tick labels in pixels labelHeight: null, reserveSpace: null, // whether to reserve space even if axis isn't shown tickLength: null, // size in pixels of ticks, or "full" for whole line alignTicksWithAxis: null, // axis number or null for no sync tickDecimals: null, // no. of decimals, null means auto tickSize: null, // number or [number, "unit"] minTickSize: null // number or [number, "unit"] }, yaxis: { autoscaleMargin: 0.02, position: "left" // or "right" }, xaxes: [], yaxes: [], series: { points: { show: false, radius: 3, lineWidth: 2, // in pixels fill: true, fillColor: "#ffffff", symbol: "circle" // or callback }, lines: { // we don't put in show: false so we can see // whether lines were actively disabled lineWidth: 2, // in pixels fill: false, fillColor: null, steps: false // Omit 'zero', so we can later default its value to // match that of the 'fill' option. }, bars: { show: false, lineWidth: 2, // in pixels barWidth: 1, // in units of the x axis fill: true, fillColor: null, align: "left", // "left", "right", or "center" horizontal: false, zero: true }, shadowSize: 3, highlightColor: null }, grid: { show: true, aboveData: false, color: "#545454", // primary color used for outline and labels backgroundColor: null, // null for transparent, else color borderColor: null, // set if different from the grid color tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" margin: 0, // distance from the canvas edge to the grid labelMargin: 5, // in pixels axisMargin: 8, // in pixels borderWidth: 2, // in pixels minBorderMargin: null, // in pixels, null means taken from points radius markings: null, // array of ranges or fn: axes -> array of ranges markingsColor: "#f4f4f4", markingsLineWidth: 2, // interactive stuff clickable: false, hoverable: false, autoHighlight: true, // highlight in case mouse is near mouseActiveRadius: 10 // how far the mouse can be away to activate an item }, interaction: { redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow }, hooks: {} }, surface = null, // the canvas for the plot itself overlay = null, // canvas for interactive stuff on top of plot eventHolder = null, // jQuery object that events should be bound to ctx = null, octx = null, xaxes = [], yaxes = [], plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, plotWidth = 0, plotHeight = 0, hooks = { processOptions: [], processRawData: [], processDatapoints: [], processOffset: [], drawBackground: [], drawSeries: [], draw: [], bindEvents: [], drawOverlay: [], shutdown: [] }, plot = this; // public functions plot.setData = setData; plot.setupGrid = setupGrid; plot.draw = draw; plot.getPlaceholder = function() { return placeholder; }; plot.getCanvas = function() { return surface.element; }; plot.getPlotOffset = function() { return plotOffset; }; plot.width = function () { return plotWidth; }; plot.height = function () { return plotHeight; }; plot.offset = function () { var o = eventHolder.offset(); o.left += plotOffset.left; o.top += plotOffset.top; return o; }; plot.getData = function () { return series; }; plot.getAxes = function () { var res = {}, i; $.each(xaxes.concat(yaxes), function (_, axis) { if (axis) res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; }); return res; }; plot.getXAxes = function () { return xaxes; }; plot.getYAxes = function () { return yaxes; }; plot.c2p = canvasToAxisCoords; plot.p2c = axisToCanvasCoords; plot.getOptions = function () { return options; }; plot.highlight = highlight; plot.unhighlight = unhighlight; plot.triggerRedrawOverlay = triggerRedrawOverlay; plot.pointOffset = function(point) { return { left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) }; }; plot.shutdown = shutdown; plot.destroy = function () { shutdown(); placeholder.removeData("plot").empty(); series = []; options = null; surface = null; overlay = null; eventHolder = null; ctx = null; octx = null; xaxes = []; yaxes = []; hooks = null; highlights = []; plot = null; }; plot.resize = function () { var width = placeholder.width(), height = placeholder.height(); surface.resize(width, height); overlay.resize(width, height); }; // public attributes plot.hooks = hooks; // initialize initPlugins(plot); parseOptions(options_); setupCanvases(); setData(data_); setupGrid(); draw(); bindEvents(); function executeHooks(hook, args) { args = [plot].concat(args); for (var i = 0; i < hook.length; ++i) hook[i].apply(this, args); } function initPlugins() { // References to key classes, allowing plugins to modify them var classes = { Canvas: Canvas }; for (var i = 0; i < plugins.length; ++i) { var p = plugins[i]; p.init(plot, classes); if (p.options) $.extend(true, options, p.options); } } function parseOptions(opts) { $.extend(true, options, opts); // $.extend merges arrays, rather than replacing them. When less // colors are provided than the size of the default palette, we // end up with those colors plus the remaining defaults, which is // not expected behavior; avoid it by replacing them here. if (opts && opts.colors) { options.colors = opts.colors; } if (options.xaxis.color == null) options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); if (options.yaxis.color == null) options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; if (options.grid.borderColor == null) options.grid.borderColor = options.grid.color; if (options.grid.tickColor == null) options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); // Fill in defaults for axis options, including any unspecified // font-spec fields, if a font-spec was provided. // If no x/y axis options were provided, create one of each anyway, // since the rest of the code assumes that they exist. var i, axisOptions, axisCount, fontSize = placeholder.css("font-size"), fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, fontDefaults = { style: placeholder.css("font-style"), size: Math.round(0.8 * fontSizeDefault), variant: placeholder.css("font-variant"), weight: placeholder.css("font-weight"), family: placeholder.css("font-family") }; axisCount = options.xaxes.length || 1; for (i = 0; i < axisCount; ++i) { axisOptions = options.xaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color; } axisOptions = $.extend(true, {}, options.xaxis, axisOptions); options.xaxes[i] = axisOptions; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); if (!axisOptions.font.color) { axisOptions.font.color = axisOptions.color; } if (!axisOptions.font.lineHeight) { axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); } } } axisCount = options.yaxes.length || 1; for (i = 0; i < axisCount; ++i) { axisOptions = options.yaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color; } axisOptions = $.extend(true, {}, options.yaxis, axisOptions); options.yaxes[i] = axisOptions; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); if (!axisOptions.font.color) { axisOptions.font.color = axisOptions.color; } if (!axisOptions.font.lineHeight) { axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); } } } // backwards compatibility, to be removed in future if (options.xaxis.noTicks && options.xaxis.ticks == null) options.xaxis.ticks = options.xaxis.noTicks; if (options.yaxis.noTicks && options.yaxis.ticks == null) options.yaxis.ticks = options.yaxis.noTicks; if (options.x2axis) { options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); options.xaxes[1].position = "top"; // Override the inherit to allow the axis to auto-scale if (options.x2axis.min == null) { options.xaxes[1].min = null; } if (options.x2axis.max == null) { options.xaxes[1].max = null; } } if (options.y2axis) { options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); options.yaxes[1].position = "right"; // Override the inherit to allow the axis to auto-scale if (options.y2axis.min == null) { options.yaxes[1].min = null; } if (options.y2axis.max == null) { options.yaxes[1].max = null; } } if (options.grid.coloredAreas) options.grid.markings = options.grid.coloredAreas; if (options.grid.coloredAreasColor) options.grid.markingsColor = options.grid.coloredAreasColor; if (options.lines) $.extend(true, options.series.lines, options.lines); if (options.points) $.extend(true, options.series.points, options.points); if (options.bars) $.extend(true, options.series.bars, options.bars); if (options.shadowSize != null) options.series.shadowSize = options.shadowSize; if (options.highlightColor != null) options.series.highlightColor = options.highlightColor; // save options on axes for future reference for (i = 0; i < options.xaxes.length; ++i) getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; for (i = 0; i < options.yaxes.length; ++i) getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; // add hooks from options for (var n in hooks) if (options.hooks[n] && options.hooks[n].length) hooks[n] = hooks[n].concat(options.hooks[n]); executeHooks(hooks.processOptions, [options]); } function setData(d) { series = parseData(d); fillInSeriesOptions(); processData(); } function parseData(d) { var res = []; for (var i = 0; i < d.length; ++i) { var s = $.extend(true, {}, options.series); if (d[i].data != null) { s.data = d[i].data; // move the data instead of deep-copy delete d[i].data; $.extend(true, s, d[i]); d[i].data = s.data; } else s.data = d[i]; res.push(s); } return res; } function axisNumber(obj, coord) { var a = obj[coord + "axis"]; if (typeof a == "object") // if we got a real axis, extract number a = a.n; if (typeof a != "number") a = 1; // default to first axis return a; } function allAxes() { // return flat array without annoying null entries return $.grep(xaxes.concat(yaxes), function (a) { return a; }); } function canvasToAxisCoords(pos) { // return an object with x/y corresponding to all used axes var res = {}, i, axis; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; if (axis && axis.used) res["x" + axis.n] = axis.c2p(pos.left); } for (i = 0; i < yaxes.length; ++i) { axis = yaxes[i]; if (axis && axis.used) res["y" + axis.n] = axis.c2p(pos.top); } if (res.x1 !== undefined) res.x = res.x1; if (res.y1 !== undefined) res.y = res.y1; return res; } function axisToCanvasCoords(pos) { // get canvas coords from the first pair of x/y found in pos var res = {}, i, axis, key; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; if (axis && axis.used) { key = "x" + axis.n; if (pos[key] == null && axis.n == 1) key = "x"; if (pos[key] != null) { res.left = axis.p2c(pos[key]); break; } } } for (i = 0; i < yaxes.length; ++i) { axis = yaxes[i]; if (axis && axis.used) { key = "y" + axis.n; if (pos[key] == null && axis.n == 1) key = "y"; if (pos[key] != null) { res.top = axis.p2c(pos[key]); break; } } } return res; } function getOrCreateAxis(axes, number) { if (!axes[number - 1]) axes[number - 1] = { n: number, // save the number for future reference direction: axes == xaxes ? "x" : "y", options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) }; return axes[number - 1]; } function fillInSeriesOptions() { var neededColors = series.length, maxIndex = -1, i; // Subtract the number of series that already have fixed colors or // color indexes from the number that we still need to generate. for (i = 0; i < series.length; ++i) { var sc = series[i].color; if (sc != null) { neededColors--; if (typeof sc == "number" && sc > maxIndex) { maxIndex = sc; } } } // If any of the series have fixed color indexes, then we need to // generate at least as many colors as the highest index. if (neededColors <= maxIndex) { neededColors = maxIndex + 1; } // Generate all the colors, using first the option colors and then // variations on those colors once they're exhausted. var c, colors = [], colorPool = options.colors, colorPoolSize = colorPool.length, variation = 0; for (i = 0; i < neededColors; i++) { c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); // Each time we exhaust the colors in the pool we adjust // a scaling factor used to produce more variations on // those colors. The factor alternates negative/positive // to produce lighter/darker colors. // Reset the variation after every few cycles, or else // it will end up producing only white or black colors. if (i % colorPoolSize == 0 && i) { if (variation >= 0) { if (variation < 0.5) { variation = -variation - 0.2; } else variation = 0; } else variation = -variation; } colors[i] = c.scale('rgb', 1 + variation); } // Finalize the series options, filling in their colors var colori = 0, s; for (i = 0; i < series.length; ++i) { s = series[i]; // assign colors if (s.color == null) { s.color = colors[colori].toString(); ++colori; } else if (typeof s.color == "number") s.color = colors[s.color].toString(); // turn on lines automatically in case nothing is set if (s.lines.show == null) { var v, show = true; for (v in s) if (s[v] && s[v].show) { show = false; break; } if (show) s.lines.show = true; } // If nothing was provided for lines.zero, default it to match // lines.fill, since areas by default should extend to zero. if (s.lines.zero == null) { s.lines.zero = !!s.lines.fill; } // setup axes s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); } } function processData() { var topSentry = Number.POSITIVE_INFINITY, bottomSentry = Number.NEGATIVE_INFINITY, fakeInfinity = Number.MAX_VALUE, i, j, k, m, length, s, points, ps, x, y, axis, val, f, p, data, format; function updateAxis(axis, min, max) { if (min < axis.datamin && min != -fakeInfinity) axis.datamin = min; if (max > axis.datamax && max != fakeInfinity) axis.datamax = max; } $.each(allAxes(), function (_, axis) { // init axis axis.datamin = topSentry; axis.datamax = bottomSentry; axis.used = false; }); for (i = 0; i < series.length; ++i) { s = series[i]; s.datapoints = { points: [] }; executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); } // first pass: clean and copy data for (i = 0; i < series.length; ++i) { s = series[i]; data = s.data; format = s.datapoints.format; if (!format) { format = []; // find out how to copy format.push({ x: true, number: true, required: true }); format.push({ y: true, number: true, required: true }); if (s.bars.show || (s.lines.show && s.lines.fill)) { var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); if (s.bars.horizontal) { delete format[format.length - 1].y; format[format.length - 1].x = true; } } s.datapoints.format = format; } if (s.datapoints.pointsize != null) continue; // already filled in s.datapoints.pointsize = format.length; ps = s.datapoints.pointsize; points = s.datapoints.points; var insertSteps = s.lines.show && s.lines.steps; s.xaxis.used = s.yaxis.used = true; for (j = k = 0; j < data.length; ++j, k += ps) { p = data[j]; var nullify = p == null; if (!nullify) { for (m = 0; m < ps; ++m) { val = p[m]; f = format[m]; if (f) { if (f.number && val != null) { val = +val; // convert to number if (isNaN(val)) val = null; else if (val == Infinity) val = fakeInfinity; else if (val == -Infinity) val = -fakeInfinity; } if (val == null) { if (f.required) nullify = true; if (f.defaultValue != null) val = f.defaultValue; } } points[k + m] = val; } } if (nullify) { for (m = 0; m < ps; ++m) { val = points[k + m]; if (val != null) { f = format[m]; // extract min/max info if (f.autoscale !== false) { if (f.x) { updateAxis(s.xaxis, val, val); } if (f.y) { updateAxis(s.yaxis, val, val); } } } points[k + m] = null; } } else { // a little bit of line specific stuff that // perhaps shouldn't be here, but lacking // better means... if (insertSteps && k > 0 && points[k - ps] != null && points[k - ps] != points[k] && points[k - ps + 1] != points[k + 1]) { // copy the point to make room for a middle point for (m = 0; m < ps; ++m) points[k + ps + m] = points[k + m]; // middle point has same y points[k + 1] = points[k - ps + 1]; // we've added a point, better reflect that k += ps; } } } } // give the hooks a chance to run for (i = 0; i < series.length; ++i) { s = series[i]; executeHooks(hooks.processDatapoints, [ s, s.datapoints]); } // second pass: find datamax/datamin for auto-scaling for (i = 0; i < series.length; ++i) { s = series[i]; points = s.datapoints.points; ps = s.datapoints.pointsize; format = s.datapoints.format; var xmin = topSentry, ymin = topSentry, xmax = bottomSentry, ymax = bottomSentry; for (j = 0; j < points.length; j += ps) { if (points[j] == null) continue; for (m = 0; m < ps; ++m) { val = points[j + m]; f = format[m]; if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) continue; if (f.x) { if (val < xmin) xmin = val; if (val > xmax) xmax = val; } if (f.y) { if (val < ymin) ymin = val; if (val > ymax) ymax = val; } } } if (s.bars.show) { // make sure we got room for the bar on the dancing floor var delta; switch (s.bars.align) { case "left": delta = 0; break; case "right": delta = -s.bars.barWidth; break; default: delta = -s.bars.barWidth / 2; } if (s.bars.horizontal) { ymin += delta; ymax += delta + s.bars.barWidth; } else { xmin += delta; xmax += delta + s.bars.barWidth; } } updateAxis(s.xaxis, xmin, xmax); updateAxis(s.yaxis, ymin, ymax); } $.each(allAxes(), function (_, axis) { if (axis.datamin == topSentry) axis.datamin = null; if (axis.datamax == bottomSentry) axis.datamax = null; }); } function setupCanvases() { // Make sure the placeholder is clear of everything except canvases // from a previous plot in this container that we'll try to re-use. placeholder.css("padding", 0) // padding messes up the positioning .children().filter(function(){ return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); }).remove(); if (placeholder.css("position") == 'static') placeholder.css("position", "relative"); // for positioning labels and overlay surface = new Canvas("flot-base", placeholder); overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features ctx = surface.context; octx = overlay.context; // define which element we're listening for events on eventHolder = $(overlay.element).unbind(); // If we're re-using a plot object, shut down the old one var existing = placeholder.data("plot"); if (existing) { existing.shutdown(); overlay.clear(); } // save in case we get replotted placeholder.data("plot", plot); } function bindEvents() { // bind events if (options.grid.hoverable) { eventHolder.mousemove(onMouseMove); // Use bind, rather than .mouseleave, because we officially // still support jQuery 1.2.6, which doesn't define a shortcut // for mouseenter or mouseleave. This was a bug/oversight that // was fixed somewhere around 1.3.x. We can return to using // .mouseleave when we drop support for 1.2.6. eventHolder.bind("mouseleave", onMouseLeave); } if (options.grid.clickable) eventHolder.click(onClick); executeHooks(hooks.bindEvents, [eventHolder]); } function shutdown() { if (redrawTimeout) clearTimeout(redrawTimeout); eventHolder.unbind("mousemove", onMouseMove); eventHolder.unbind("mouseleave", onMouseLeave); eventHolder.unbind("click", onClick); executeHooks(hooks.shutdown, [eventHolder]); } function setTransformationHelpers(axis) { // set helper functions on the axis, assumes plot area // has been computed already function identity(x) { return x; } var s, m, t = axis.options.transform || identity, it = axis.options.inverseTransform; // precompute how much the axis is scaling a point // in canvas space if (axis.direction == "x") { s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); m = Math.min(t(axis.max), t(axis.min)); } else { s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); s = -s; m = Math.max(t(axis.max), t(axis.min)); } // data point to canvas coordinate if (t == identity) // slight optimization axis.p2c = function (p) { return (p - m) * s; }; else axis.p2c = function (p) { return (t(p) - m) * s; }; // canvas coordinate to data point if (!it) axis.c2p = function (c) { return m + c / s; }; else axis.c2p = function (c) { return it(m + c / s); }; } function measureTickLabels(axis) { var opts = axis.options, ticks = axis.ticks || [], labelWidth = opts.labelWidth || 0, labelHeight = opts.labelHeight || 0, maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = opts.font || "flot-tick-label tickLabel"; for (var i = 0; i < ticks.length; ++i) { var t = ticks[i]; if (!t.label) continue; var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); labelWidth = Math.max(labelWidth, info.width); labelHeight = Math.max(labelHeight, info.height); } axis.labelWidth = opts.labelWidth || labelWidth; axis.labelHeight = opts.labelHeight || labelHeight; } function allocateAxisBoxFirstPhase(axis) { // find the bounding box of the axis by looking at label // widths/heights and ticks, make room by diminishing the // plotOffset; this first phase only looks at one // dimension per axis, the other dimension depends on the // other axes so will have to wait var lw = axis.labelWidth, lh = axis.labelHeight, pos = axis.options.position, isXAxis = axis.direction === "x", tickLength = axis.options.tickLength, axisMargin = options.grid.axisMargin, padding = options.grid.labelMargin, innermost = true, outermost = true, first = true, found = false; // Determine the axis's position in its direction and on its side $.each(isXAxis ? xaxes : yaxes, function(i, a) { if (a && (a.show || a.reserveSpace)) { if (a === axis) { found = true; } else if (a.options.position === pos) { if (found) { outermost = false; } else { innermost = false; } } if (!found) { first = false; } } }); // The outermost axis on each side has no margin if (outermost) { axisMargin = 0; } // The ticks for the first axis in each direction stretch across if (tickLength == null) { tickLength = first ? "full" : 5; } if (!isNaN(+tickLength)) padding += +tickLength; if (isXAxis) { lh += padding; if (pos == "bottom") { plotOffset.bottom += lh + axisMargin; axis.box = { top: surface.height - plotOffset.bottom, height: lh }; } else { axis.box = { top: plotOffset.top + axisMargin, height: lh }; plotOffset.top += lh + axisMargin; } } else { lw += padding; if (pos == "left") { axis.box = { left: plotOffset.left + axisMargin, width: lw }; plotOffset.left += lw + axisMargin; } else { plotOffset.right += lw + axisMargin; axis.box = { left: surface.width - plotOffset.right, width: lw }; } } // save for future reference axis.position = pos; axis.tickLength = tickLength; axis.box.padding = padding; axis.innermost = innermost; } function allocateAxisBoxSecondPhase(axis) { // now that all axis boxes have been placed in one // dimension, we can set the remaining dimension coordinates if (axis.direction == "x") { axis.box.left = plotOffset.left - axis.labelWidth / 2; axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; } else { axis.box.top = plotOffset.top - axis.labelHeight / 2; axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; } } function adjustLayoutForThingsStickingOut() { // possibly adjust plot offset to ensure everything stays // inside the canvas and isn't clipped off var minMargin = options.grid.minBorderMargin, axis, i; // check stuff from the plot (FIXME: this should just read // a value from the series, otherwise it's impossible to // customize) if (minMargin == null) { minMargin = 0; for (i = 0; i < series.length; ++i) minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); } var margins = { left: minMargin, right: minMargin, top: minMargin, bottom: minMargin }; // check axis labels, note we don't check the actual // labels but instead use the overall width/height to not // jump as much around with replots $.each(allAxes(), function (_, axis) { if (axis.reserveSpace && axis.ticks && axis.ticks.length) { if (axis.direction === "x") { margins.left = Math.max(margins.left, axis.labelWidth / 2); margins.right = Math.max(margins.right, axis.labelWidth / 2); } else { margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); margins.top = Math.max(margins.top, axis.labelHeight / 2); } } }); plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); } function setupGrid() { var i, axes = allAxes(), showGrid = options.grid.show; // Initialize the plot's offset from the edge of the canvas for (var a in plotOffset) { var margin = options.grid.margin || 0; plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; } executeHooks(hooks.processOffset, [plotOffset]); // If the grid is visible, add its border width to the offset for (var a in plotOffset) { if(typeof(options.grid.borderWidth) == "object") { plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; } else { plotOffset[a] += showGrid ? options.grid.borderWidth : 0; } } $.each(axes, function (_, axis) { var axisOpts = axis.options; axis.show = axisOpts.show == null ? axis.used : axisOpts.show; axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; setRange(axis); }); if (showGrid) { var allocatedAxes = $.grep(axes, function (axis) { return axis.show || axis.reserveSpace; }); $.each(allocatedAxes, function (_, axis) { // make the ticks setupTickGeneration(axis); setTicks(axis); snapRangeToTicks(axis, axis.ticks); // find labelWidth/Height for axis measureTickLabels(axis); }); // with all dimensions calculated, we can compute the // axis bounding boxes, start from the outside // (reverse order) for (i = allocatedAxes.length - 1; i >= 0; --i) allocateAxisBoxFirstPhase(allocatedAxes[i]); // make sure we've got enough space for things that // might stick out adjustLayoutForThingsStickingOut(); $.each(allocatedAxes, function (_, axis) { allocateAxisBoxSecondPhase(axis); }); } plotWidth = surface.width - plotOffset.left - plotOffset.right; plotHeight = surface.height - plotOffset.bottom - plotOffset.top; // now we got the proper plot dimensions, we can compute the scaling $.each(axes, function (_, axis) { setTransformationHelpers(axis); }); if (showGrid) { drawAxisLabels(); } insertLegend(); } function setRange(axis) { var opts = axis.options, min = +(opts.min != null ? opts.min : axis.datamin), max = +(opts.max != null ? opts.max : axis.datamax), delta = max - min; if (delta == 0.0) { // degenerate case var widen = max == 0 ? 1 : 0.01; if (opts.min == null) min -= widen; // always widen max if we couldn't widen min to ensure we // don't fall into min == max which doesn't work if (opts.max == null || opts.min != null) max += widen; } else { // consider autoscaling var margin = opts.autoscaleMargin; if (margin != null) { if (opts.min == null) { min -= delta * margin; // make sure we don't go below zero if all values // are positive if (min < 0 && axis.datamin != null && axis.datamin >= 0) min = 0; } if (opts.max == null) { max += delta * margin; if (max > 0 && axis.datamax != null && axis.datamax <= 0) max = 0; } } } axis.min = min; axis.max = max; } function setupTickGeneration(axis) { var opts = axis.options; // estimate number of ticks var noTicks; if (typeof opts.ticks == "number" && opts.ticks > 0) noTicks = opts.ticks; else // heuristic based on the model a*sqrt(x) fitted to // some data points that seemed reasonable noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); var delta = (axis.max - axis.min) / noTicks, dec = -Math.floor(Math.log(delta) / Math.LN10), maxDec = opts.tickDecimals; if (maxDec != null && dec > maxDec) { dec = maxDec; } var magn = Math.pow(10, -dec), norm = delta / magn, // norm is between 1.0 and 10.0 size; if (norm < 1.5) { size = 1; } else if (norm < 3) { size = 2; // special case for 2.5, requires an extra decimal if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { size = 2.5; ++dec; } } else if (norm < 7.5) { size = 5; } else { size = 10; } size *= magn; if (opts.minTickSize != null && size < opts.minTickSize) { size = opts.minTickSize; } axis.delta = delta; axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); axis.tickSize = opts.tickSize || size; // Time mode was moved to a plug-in in 0.8, and since so many people use it // we'll add an especially friendly reminder to make sure they included it. if (opts.mode == "time" && !axis.tickGenerator) { throw new Error("Time mode requires the flot.time plugin."); } // Flot supports base-10 axes; any other mode else is handled by a plug-in, // like flot.time.js. if (!axis.tickGenerator) { axis.tickGenerator = function (axis) { var ticks = [], start = floorInBase(axis.min, axis.tickSize), i = 0, v = Number.NaN, prev; do { prev = v; v = start + i * axis.tickSize; ticks.push(v); ++i; } while (v < axis.max && v != prev); return ticks; }; axis.tickFormatter = function (value, axis) { var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; var formatted = "" + Math.round(value * factor) / factor; // If tickDecimals was specified, ensure that we have exactly that // much precision; otherwise default to the value's own precision. if (axis.tickDecimals != null) { var decimal = formatted.indexOf("."); var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; if (precision < axis.tickDecimals) { return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); } } return formatted; }; } if ($.isFunction(opts.tickFormatter)) axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; if (opts.alignTicksWithAxis != null) { var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; if (otherAxis && otherAxis.used && otherAxis != axis) { // consider snapping min/max to outermost nice ticks var niceTicks = axis.tickGenerator(axis); if (niceTicks.length > 0) { if (opts.min == null) axis.min = Math.min(axis.min, niceTicks[0]); if (opts.max == null && niceTicks.length > 1) axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); } axis.tickGenerator = function (axis) { // copy ticks, scaled to this axis var ticks = [], v, i; for (i = 0; i < otherAxis.ticks.length; ++i) { v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); v = axis.min + v * (axis.max - axis.min); ticks.push(v); } return ticks; }; // we might need an extra decimal since forced // ticks don't necessarily fit naturally if (!axis.mode && opts.tickDecimals == null) { var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), ts = axis.tickGenerator(axis); // only proceed if the tick interval rounded // with an extra decimal doesn't give us a // zero at end if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) axis.tickDecimals = extraDec; } } } } function setTicks(axis) { var oticks = axis.options.ticks, ticks = []; if (oticks == null || (typeof oticks == "number" && oticks > 0)) ticks = axis.tickGenerator(axis); else if (oticks) { if ($.isFunction(oticks)) // generate the ticks ticks = oticks(axis); else ticks = oticks; } // clean up/labelify the supplied ticks, copy them over var i, v; axis.ticks = []; for (i = 0; i < ticks.length; ++i) { var label = null; var t = ticks[i]; if (typeof t == "object") { v = +t[0]; if (t.length > 1) label = t[1]; } else v = +t; if (label == null) label = axis.tickFormatter(v, axis); if (!isNaN(v)) axis.ticks.push({ v: v, label: label }); } } function snapRangeToTicks(axis, ticks) { if (axis.options.autoscaleMargin && ticks.length > 0) { // snap to ticks if (axis.options.min == null) axis.min = Math.min(axis.min, ticks[0].v); if (axis.options.max == null && ticks.length > 1) axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); } } function draw() { surface.clear(); executeHooks(hooks.drawBackground, [ctx]); var grid = options.grid; // draw background, if any if (grid.show && grid.backgroundColor) drawBackground(); if (grid.show && !grid.aboveData) { drawGrid(); } for (var i = 0; i < series.length; ++i) { executeHooks(hooks.drawSeries, [ctx, series[i]]); drawSeries(series[i]); } executeHooks(hooks.draw, [ctx]); if (grid.show && grid.aboveData) { drawGrid(); } surface.render(); // A draw implies that either the axes or data have changed, so we // should probably update the overlay highlights as well. triggerRedrawOverlay(); } function extractRange(ranges, coord) { var axis, from, to, key, axes = allAxes(); for (var i = 0; i < axes.length; ++i) { axis = axes[i]; if (axis.direction == coord) { key = coord + axis.n + "axis"; if (!ranges[key] && axis.n == 1) key = coord + "axis"; // support x1axis as xaxis if (ranges[key]) { from = ranges[key].from; to = ranges[key].to; break; } } } // backwards-compat stuff - to be removed in future if (!ranges[key]) { axis = coord == "x" ? xaxes[0] : yaxes[0]; from = ranges[coord + "1"]; to = ranges[coord + "2"]; } // auto-reverse as an added bonus if (from != null && to != null && from > to) { var tmp = from; from = to; to = tmp; } return { from: from, to: to, axis: axis }; } function drawBackground() { ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); ctx.fillRect(0, 0, plotWidth, plotHeight); ctx.restore(); } function drawGrid() { var i, axes, bw, bc; ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); // draw markings var markings = options.grid.markings; if (markings) { if ($.isFunction(markings)) { axes = plot.getAxes(); // xmin etc. is backwards compatibility, to be // removed in the future axes.xmin = axes.xaxis.min; axes.xmax = axes.xaxis.max; axes.ymin = axes.yaxis.min; axes.ymax = axes.yaxis.max; markings = markings(axes); } for (i = 0; i < markings.length; ++i) { var m = markings[i], xrange = extractRange(m, "x"), yrange = extractRange(m, "y"); // fill in missing if (xrange.from == null) xrange.from = xrange.axis.min; if (xrange.to == null) xrange.to = xrange.axis.max; if (yrange.from == null) yrange.from = yrange.axis.min; if (yrange.to == null) yrange.to = yrange.axis.max; // clip if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) continue; xrange.from = Math.max(xrange.from, xrange.axis.min); xrange.to = Math.min(xrange.to, xrange.axis.max); yrange.from = Math.max(yrange.from, yrange.axis.min); yrange.to = Math.min(yrange.to, yrange.axis.max); var xequal = xrange.from === xrange.to, yequal = yrange.from === yrange.to; if (xequal && yequal) { continue; } // then draw xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); if (xequal || yequal) { var lineWidth = m.lineWidth || options.grid.markingsLineWidth, subPixel = lineWidth % 2 ? 0.5 : 0; ctx.beginPath(); ctx.strokeStyle = m.color || options.grid.markingsColor; ctx.lineWidth = lineWidth; if (xequal) { ctx.moveTo(xrange.to + subPixel, yrange.from); ctx.lineTo(xrange.to + subPixel, yrange.to); } else { ctx.moveTo(xrange.from, yrange.to + subPixel); ctx.lineTo(xrange.to, yrange.to + subPixel); } ctx.stroke(); } else { ctx.fillStyle = m.color || options.grid.markingsColor; ctx.fillRect(xrange.from, yrange.to, xrange.to - xrange.from, yrange.from - yrange.to); } } } // draw the ticks axes = allAxes(); bw = options.grid.borderWidth; for (var j = 0; j < axes.length; ++j) { var axis = axes[j], box = axis.box, t = axis.tickLength, x, y, xoff, yoff; if (!axis.show || axis.ticks.length == 0) continue; ctx.lineWidth = 1; // find the edges if (axis.direction == "x") { x = 0; if (t == "full") y = (axis.position == "top" ? 0 : plotHeight); else y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); } else { y = 0; if (t == "full") x = (axis.position == "left" ? 0 : plotWidth); else x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); } // draw tick bar if (!axis.innermost) { ctx.strokeStyle = axis.options.color; ctx.beginPath(); xoff = yoff = 0; if (axis.direction == "x") xoff = plotWidth + 1; else yoff = plotHeight + 1; if (ctx.lineWidth == 1) { if (axis.direction == "x") { y = Math.floor(y) + 0.5; } else { x = Math.floor(x) + 0.5; } } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff); ctx.stroke(); } // draw ticks ctx.strokeStyle = axis.options.tickColor; ctx.beginPath(); for (i = 0; i < axis.ticks.length; ++i) { var v = axis.ticks[i].v; xoff = yoff = 0; if (isNaN(v) || v < axis.min || v > axis.max // skip those lying on the axes if we got a border || (t == "full" && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) && (v == axis.min || v == axis.max))) continue; if (axis.direction == "x") { x = axis.p2c(v); yoff = t == "full" ? -plotHeight : t; if (axis.position == "top") yoff = -yoff; } else { y = axis.p2c(v); xoff = t == "full" ? -plotWidth : t; if (axis.position == "left") xoff = -xoff; } if (ctx.lineWidth == 1) { if (axis.direction == "x") x = Math.floor(x) + 0.5; else y = Math.floor(y) + 0.5; } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff); } ctx.stroke(); } // draw border if (bw) { // If either borderWidth or borderColor is an object, then draw the border // line by line instead of as one rectangle bc = options.grid.borderColor; if(typeof bw == "object" || typeof bc == "object") { if (typeof bw !== "object") { bw = {top: bw, right: bw, bottom: bw, left: bw}; } if (typeof bc !== "object") { bc = {top: bc, right: bc, bottom: bc, left: bc}; } if (bw.top > 0) { ctx.strokeStyle = bc.top; ctx.lineWidth = bw.top; ctx.beginPath(); ctx.moveTo(0 - bw.left, 0 - bw.top/2); ctx.lineTo(plotWidth, 0 - bw.top/2); ctx.stroke(); } if (bw.right > 0) { ctx.strokeStyle = bc.right; ctx.lineWidth = bw.right; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); ctx.lineTo(plotWidth + bw.right / 2, plotHeight); ctx.stroke(); } if (bw.bottom > 0) { ctx.strokeStyle = bc.bottom; ctx.lineWidth = bw.bottom; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); ctx.lineTo(0, plotHeight + bw.bottom / 2); ctx.stroke(); } if (bw.left > 0) { ctx.strokeStyle = bc.left; ctx.lineWidth = bw.left; ctx.beginPath(); ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom); ctx.lineTo(0- bw.left/2, 0); ctx.stroke(); } } else { ctx.lineWidth = bw; ctx.strokeStyle = options.grid.borderColor; ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); } } ctx.restore(); } function drawAxisLabels() { $.each(allAxes(), function (_, axis) { var box = axis.box, legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = axis.options.font || "flot-tick-label tickLabel", tick, x, y, halign, valign; // Remove text before checking for axis.show and ticks.length; // otherwise plugins, like flot-tickrotor, that draw their own // tick labels will end up with both theirs and the defaults. surface.removeText(layer); if (!axis.show || axis.ticks.length == 0) return; for (var i = 0; i < axis.ticks.length; ++i) { tick = axis.ticks[i]; if (!tick.label || tick.v < axis.min || tick.v > axis.max) continue; if (axis.direction == "x") { halign = "center"; x = plotOffset.left + axis.p2c(tick.v); if (axis.position == "bottom") { y = box.top + box.padding; } else { y = box.top + box.height - box.padding; valign = "bottom"; } } else { valign = "middle"; y = plotOffset.top + axis.p2c(tick.v); if (axis.position == "left") { x = box.left + box.width - box.padding; halign = "right"; } else { x = box.left + box.padding; } } surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); } }); } function drawSeries(series) { if (series.lines.show) drawSeriesLines(series); if (series.bars.show) drawSeriesBars(series); if (series.points.show) drawSeriesPoints(series); } function drawSeriesLines(series) { function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, prevx = null, prevy = null; ctx.beginPath(); for (var i = ps; i < points.length; i += ps) { var x1 = points[i - ps], y1 = points[i - ps + 1], x2 = points[i], y2 = points[i + 1]; if (x1 == null || x2 == null) continue; // clip with ymin if (y1 <= y2 && y1 < axisy.min) { if (y2 < axisy.min) continue; // line segment is outside // compute new intersection point x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min; } else if (y2 <= y1 && y2 < axisy.min) { if (y1 < axisy.min) continue; x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min; } // clip with ymax if (y1 >= y2 && y1 > axisy.max) { if (y2 > axisy.max) continue; x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max; } else if (y2 >= y1 && y2 > axisy.max) { if (y1 > axisy.max) continue; x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max; } // clip with xmin if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min; } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min; } // clip with xmax if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max; } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max; } if (x1 != prevx || y1 != prevy) ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); prevx = x2; prevy = y2; ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); } ctx.stroke(); } function plotLineArea(datapoints, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, bottom = Math.min(Math.max(0, axisy.min), axisy.max), i = 0, top, areaOpen = false, ypos = 1, segmentStart = 0, segmentEnd = 0; // we process each segment in two turns, first forward // direction to sketch out top, then once we hit the // end we go backwards to sketch the bottom while (true) { if (ps > 0 && i > points.length + ps) break; i += ps; // ps is negative if going backwards var x1 = points[i - ps], y1 = points[i - ps + ypos], x2 = points[i], y2 = points[i + ypos]; if (areaOpen) { if (ps > 0 && x1 != null && x2 == null) { // at turning point segmentEnd = i; ps = -ps; ypos = 2; continue; } if (ps < 0 && i == segmentStart + ps) { // done with the reverse sweep ctx.fill(); areaOpen = false; ps = -ps; ypos = 1; i = segmentStart = segmentEnd + ps; continue; } } if (x1 == null || x2 == null) continue; // clip x values // clip with xmin if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min; } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min; } // clip with xmax if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max; } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max; } if (!areaOpen) { // open area ctx.beginPath(); ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); areaOpen = true; } // now first check the case where both is outside if (y1 >= axisy.max && y2 >= axisy.max) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); continue; } else if (y1 <= axisy.min && y2 <= axisy.min) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); continue; } // else it's a bit more complicated, there might // be a flat maxed out rectangle first, then a // triangular cutout or reverse; to find these // keep track of the current x values var x1old = x1, x2old = x2; // clip the y values, without shortcutting, we // go through all cases in turn // clip with ymin if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min; } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min; } // clip with ymax if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max; } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max; } // if the x value was changed we got a rectangle // to fill if (x1 != x1old) { ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); // it goes to (x1, y1), but we fill that below } // fill triangular section, this sometimes result // in redundant points if (x1, y1) hasn't changed // from previous line to, but we just ignore that ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); // fill the other rectangle if it's there if (x2 != x2old) { ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); } } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineJoin = "round"; var lw = series.lines.lineWidth, sw = series.shadowSize; // FIXME: consider another form of shadow when filling is turned on if (lw > 0 && sw > 0) { // draw shadow as a thick and thin line with transparency ctx.lineWidth = sw; ctx.strokeStyle = "rgba(0,0,0,0.1)"; // position shadow at angle from the mid of line var angle = Math.PI/18; plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); ctx.lineWidth = sw/2; plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); } ctx.lineWidth = lw; ctx.strokeStyle = series.color; var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); if (fillStyle) { ctx.fillStyle = fillStyle; plotLineArea(series.datapoints, series.xaxis, series.yaxis); } if (lw > 0) plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); ctx.restore(); } function drawSeriesPoints(series) { function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) continue; ctx.beginPath(); x = axisx.p2c(x); y = axisy.p2c(y) + offset; if (symbol == "circle") ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); else symbol(ctx, x, y, radius, shadow); ctx.closePath(); if (fillStyle) { ctx.fillStyle = fillStyle; ctx.fill(); } ctx.stroke(); } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); var lw = series.points.lineWidth, sw = series.shadowSize, radius = series.points.radius, symbol = series.points.symbol; // If the user sets the line width to 0, we change it to a very // small value. A line width of 0 seems to force the default of 1. // Doing the conditional here allows the shadow setting to still be // optional even with a lineWidth of 0. if( lw == 0 ) lw = 0.0001; if (lw > 0 && sw > 0) { // draw shadow in two steps var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; plotPoints(series.datapoints, radius, null, w + w/2, true, series.xaxis, series.yaxis, symbol); ctx.strokeStyle = "rgba(0,0,0,0.2)"; plotPoints(series.datapoints, radius, null, w/2, true, series.xaxis, series.yaxis, symbol); } ctx.lineWidth = lw; ctx.strokeStyle = series.color; plotPoints(series.datapoints, radius, getFillStyle(series.points, series.color), 0, false, series.xaxis, series.yaxis, symbol); ctx.restore(); } function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { var left, right, bottom, top, drawLeft, drawRight, drawTop, drawBottom, tmp; // in horizontal mode, we start the bar from the left // instead of from the bottom so it appears to be // horizontal rather than vertical if (horizontal) { drawBottom = drawRight = drawTop = true; drawLeft = false; left = b; right = x; top = y + barLeft; bottom = y + barRight; // account for negative bars if (right < left) { tmp = right; right = left; left = tmp; drawLeft = true; drawRight = false; } } else { drawLeft = drawRight = drawTop = true; drawBottom = false; left = x + barLeft; right = x + barRight; bottom = b; top = y; // account for negative bars if (top < bottom) { tmp = top; top = bottom; bottom = tmp; drawBottom = true; drawTop = false; } } // clip if (right < axisx.min || left > axisx.max || top < axisy.min || bottom > axisy.max) return; if (left < axisx.min) { left = axisx.min; drawLeft = false; } if (right > axisx.max) { right = axisx.max; drawRight = false; } if (bottom < axisy.min) { bottom = axisy.min; drawBottom = false; } if (top > axisy.max) { top = axisy.max; drawTop = false; } left = axisx.p2c(left); bottom = axisy.p2c(bottom); right = axisx.p2c(right); top = axisy.p2c(top); // fill the bar if (fillStyleCallback) { c.fillStyle = fillStyleCallback(bottom, top); c.fillRect(left, top, right - left, bottom - top) } // draw outline if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { c.beginPath(); // FIXME: inline moveTo is buggy with excanvas c.moveTo(left, bottom); if (drawLeft) c.lineTo(left, top); else c.moveTo(left, top); if (drawTop) c.lineTo(right, top); else c.moveTo(right, top); if (drawRight) c.lineTo(right, bottom); else c.moveTo(right, bottom); if (drawBottom) c.lineTo(left, bottom); else c.moveTo(left, bottom); c.stroke(); } } function drawSeriesBars(series) { function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { if (points[i] == null) continue; drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); // FIXME: figure out a way to add shadows (for instance along the right edge) ctx.lineWidth = series.bars.lineWidth; ctx.strokeStyle = series.color; var barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2; } var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); ctx.restore(); } function getFillStyle(filloptions, seriesColor, bottom, top) { var fill = filloptions.fill; if (!fill) return null; if (filloptions.fillColor) return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); var c = $.color.parse(seriesColor); c.a = typeof fill == "number" ? fill : 0.4; c.normalize(); return c.toString(); } function insertLegend() { if (options.legend.container != null) { $(options.legend.container).html(""); } else { placeholder.find(".legend").remove(); } if (!options.legend.show) { return; } var fragments = [], entries = [], rowStarted = false, lf = options.legend.labelFormatter, s, label; // Build a list of legend entries, with each having a label and a color for (var i = 0; i < series.length; ++i) { s = series[i]; if (s.label) { label = lf ? lf(s.label, s) : s.label; if (label) { entries.push({ label: label, color: s.color }); } } } // Sort the legend using either the default or a custom comparator if (options.legend.sorted) { if ($.isFunction(options.legend.sorted)) { entries.sort(options.legend.sorted); } else if (options.legend.sorted == "reverse") { entries.reverse(); } else { var ascending = options.legend.sorted != "descending"; entries.sort(function(a, b) { return a.label == b.label ? 0 : ( (a.label < b.label) != ascending ? 1 : -1 // Logical XOR ); }); } } // Generate markup for the list of entries, in their final order for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (i % options.legend.noColumns == 0) { if (rowStarted) fragments.push(''); fragments.push('
                                  '); rowStarted = true; } fragments.push( '' + '' ); } if (rowStarted) fragments.push(''); if (fragments.length == 0) return; var table = '
                                  正在努力地加载数据中,请稍候……
                                  没有找到匹配的记录
                                  '+xhr.responseText+'
                                  `s and ``s. @table-cell-padding: 8px; //** Padding for cells in `.table-condensed`. @table-condensed-cell-padding: 5px; //** Default background color used for all tables. @table-bg: transparent; //** Background color used for `.table-striped`. @table-bg-accent: #f9f9f9; //** Background color used for `.table-hover`. @table-bg-hover: #f5f5f5; @table-bg-active: @table-bg-hover; //** Border color for table and cell borders. @table-border-color: #ddd; //== Buttons // //## For each of Bootstrap's buttons, define text, background and border color. @btn-font-weight: normal; @btn-default-color: #333; @btn-default-bg: #fff; @btn-default-border: #ccc; @btn-primary-color: #fff; @btn-primary-bg: @brand-primary; @btn-primary-border: darken(@btn-primary-bg, 5%); @btn-success-color: #fff; @btn-success-bg: @brand-success; @btn-success-border: darken(@btn-success-bg, 5%); @btn-info-color: #fff; @btn-info-bg: @brand-info; @btn-info-border: darken(@btn-info-bg, 5%); @btn-warning-color: #fff; @btn-warning-bg: @brand-warning; @btn-warning-border: darken(@btn-warning-bg, 5%); @btn-danger-color: #fff; @btn-danger-bg: @brand-danger; @btn-danger-border: darken(@btn-danger-bg, 5%); @btn-link-disabled-color: @gray-light; //== Forms // //## //** `` background color @input-bg: #fff; //** `` background color @input-bg-disabled: @gray-lighter; //** Text color for ``s @input-color: @gray; //** `` border color @input-border: #ccc; // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4 //** Default `.form-control` border radius // This has no effect on ``s in CSS. @input-border-radius: @border-radius-base; //** Large `.form-control` border radius @input-border-radius-large: @border-radius-large; //** Small `.form-control` border radius @input-border-radius-small: @border-radius-small; //** Border color for inputs on focus @input-border-focus: #66afe9; //** Placeholder text color @input-color-placeholder: #999; //** Default `.form-control` height @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); //** Large `.form-control` height @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); //** Small `.form-control` height @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); //** `.form-group` margin @form-group-margin-bottom: 15px; @legend-color: @gray-dark; @legend-border-color: #e5e5e5; //** Background color for textual input addons @input-group-addon-bg: @gray-lighter; //** Border color for textual input addons @input-group-addon-border-color: @input-border; //** Disabled cursor for form controls and buttons. @cursor-disabled: not-allowed; //== Dropdowns // //## Dropdown menu container and contents. //** Background for the dropdown menu. @dropdown-bg: #fff; //** Dropdown menu `border-color`. @dropdown-border: rgba(0,0,0,.15); //** Dropdown menu `border-color` **for IE8**. @dropdown-fallback-border: #ccc; //** Divider color for between dropdown items. @dropdown-divider-bg: #e5e5e5; //** Dropdown link text color. @dropdown-link-color: @gray-dark; //** Hover color for dropdown links. @dropdown-link-hover-color: darken(@gray-dark, 5%); //** Hover background for dropdown links. @dropdown-link-hover-bg: #f5f5f5; //** Active dropdown menu item text color. @dropdown-link-active-color: @component-active-color; //** Active dropdown menu item background color. @dropdown-link-active-bg: @component-active-bg; //** Disabled dropdown menu item background color. @dropdown-link-disabled-color: @gray-light; //** Text color for headers within dropdown menus. @dropdown-header-color: @gray-light; //** Deprecated `@dropdown-caret-color` as of v3.1.0 @dropdown-caret-color: #000; //-- Z-index master list // // Warning: Avoid customizing these values. They're used for a bird's eye view // of components dependent on the z-axis and are designed to all work together. // // Note: These variables are not generated into the Customizer. @zindex-navbar: 1000; @zindex-dropdown: 1000; @zindex-popover: 1060; @zindex-tooltip: 1070; @zindex-navbar-fixed: 1030; @zindex-modal-background: 1040; @zindex-modal: 1050; //== Media queries breakpoints // //## Define the breakpoints at which your layout will change, adapting to different screen sizes. // Extra small screen / phone //** Deprecated `@screen-xs` as of v3.0.1 @screen-xs: 480px; //** Deprecated `@screen-xs-min` as of v3.2.0 @screen-xs-min: @screen-xs; //** Deprecated `@screen-phone` as of v3.0.1 @screen-phone: @screen-xs-min; // Small screen / tablet //** Deprecated `@screen-sm` as of v3.0.1 @screen-sm: 768px; @screen-sm-min: @screen-sm; //** Deprecated `@screen-tablet` as of v3.0.1 @screen-tablet: @screen-sm-min; // Medium screen / desktop //** Deprecated `@screen-md` as of v3.0.1 @screen-md: 992px; @screen-md-min: @screen-md; //** Deprecated `@screen-desktop` as of v3.0.1 @screen-desktop: @screen-md-min; // Large screen / wide desktop //** Deprecated `@screen-lg` as of v3.0.1 @screen-lg: 1200px; @screen-lg-min: @screen-lg; //** Deprecated `@screen-lg-desktop` as of v3.0.1 @screen-lg-desktop: @screen-lg-min; // So media queries don't overlap when required, provide a maximum @screen-xs-max: (@screen-sm-min - 1); @screen-sm-max: (@screen-md-min - 1); @screen-md-max: (@screen-lg-min - 1); //== Grid system // //## Define your custom responsive grid. //** Number of columns in the grid. @grid-columns: 12; //** Padding between columns. Gets divided in half for the left and right. @grid-gutter-width: 24px; // Navbar collapse //** Point at which the navbar becomes uncollapsed. @grid-float-breakpoint: @screen-sm-min; //** Point at which the navbar begins collapsing. @grid-float-breakpoint-max: (@grid-float-breakpoint - 1); //== Container sizes // //## Define the maximum width of `.container` for different screen sizes. // Small screen / tablet @container-tablet: (720px + @grid-gutter-width); //** For `@screen-sm-min` and up. @container-sm: @container-tablet; // Medium screen / desktop @container-desktop: (940px + @grid-gutter-width); //** For `@screen-md-min` and up. @container-md: @container-desktop; // Large screen / wide desktop @container-large-desktop: (1140px + @grid-gutter-width); //** For `@screen-lg-min` and up. @container-lg: @container-large-desktop; //== Navbar // //## // Basics of a navbar @navbar-height: 50px; @navbar-margin-bottom: @line-height-computed; @navbar-border-radius: @border-radius-base; @navbar-padding-horizontal: floor((@grid-gutter-width / 2)); @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); @navbar-collapse-max-height: 340px; @navbar-default-color: #777; @navbar-default-bg: #f8f8f8; @navbar-default-border: darken(@navbar-default-bg, 6.5%); // Navbar links @navbar-default-link-color: #777; @navbar-default-link-hover-color: #333; @navbar-default-link-hover-bg: transparent; @navbar-default-link-active-color: #555; @navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%); @navbar-default-link-disabled-color: #ccc; @navbar-default-link-disabled-bg: transparent; // Navbar brand label @navbar-default-brand-color: @navbar-default-link-color; @navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%); @navbar-default-brand-hover-bg: transparent; // Navbar toggle @navbar-default-toggle-hover-bg: #ddd; @navbar-default-toggle-icon-bar-bg: #888; @navbar-default-toggle-border-color: #ddd; // Inverted navbar // Reset inverted navbar basics @navbar-inverse-color: lighten(@gray-light, 15%); @navbar-inverse-bg: #222; @navbar-inverse-border: darken(@navbar-inverse-bg, 10%); // Inverted navbar links @navbar-inverse-link-color: lighten(@gray-light, 15%); @navbar-inverse-link-hover-color: #fff; @navbar-inverse-link-hover-bg: transparent; @navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; @navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); @navbar-inverse-link-disabled-color: #444; @navbar-inverse-link-disabled-bg: transparent; // Inverted navbar brand label @navbar-inverse-brand-color: @navbar-inverse-link-color; @navbar-inverse-brand-hover-color: #fff; @navbar-inverse-brand-hover-bg: transparent; // Inverted navbar toggle @navbar-inverse-toggle-hover-bg: #333; @navbar-inverse-toggle-icon-bar-bg: #fff; @navbar-inverse-toggle-border-color: #333; //== Navs // //## //=== Shared nav styles @nav-link-padding: 10px 15px; @nav-link-hover-bg: @gray-lighter; @nav-disabled-link-color: @gray-light; @nav-disabled-link-hover-color: @gray-light; //== Tabs @nav-tabs-border-color: #ddd; @nav-tabs-link-hover-border-color: @gray-lighter; @nav-tabs-active-link-hover-bg: @body-bg; @nav-tabs-active-link-hover-color: @gray; @nav-tabs-active-link-hover-border-color: #ddd; @nav-tabs-justified-link-border-color: #ddd; @nav-tabs-justified-active-link-border-color: @body-bg; //== Pills @nav-pills-border-radius: @border-radius-base; @nav-pills-active-link-hover-bg: @component-active-bg; @nav-pills-active-link-hover-color: @component-active-color; //== Pagination // //## @pagination-color: @link-color; @pagination-bg: #fff; @pagination-border: #ddd; @pagination-hover-color: @link-hover-color; @pagination-hover-bg: @gray-lighter; @pagination-hover-border: #ddd; @pagination-active-color: #fff; @pagination-active-bg: @brand-primary; @pagination-active-border: @brand-primary; @pagination-disabled-color: @gray-light; @pagination-disabled-bg: #fff; @pagination-disabled-border: #ddd; //== Pager // //## @pager-bg: @pagination-bg; @pager-border: @pagination-border; @pager-border-radius: 15px; @pager-hover-bg: @pagination-hover-bg; @pager-active-bg: @pagination-active-bg; @pager-active-color: @pagination-active-color; @pager-disabled-color: @pagination-disabled-color; //== Jumbotron // //## @jumbotron-padding: 30px; @jumbotron-color: inherit; @jumbotron-bg: @gray-lighter; @jumbotron-heading-color: inherit; @jumbotron-font-size: ceil((@font-size-base * 1.5)); @jumbotron-heading-font-size: ceil((@font-size-base * 4.5)); //== Form states and alerts // //## Define colors for form feedback states and, by default, alerts. @state-success-text: #3c763d; @state-success-bg: #dff0d8; @state-success-border: darken(spin(@state-success-bg, -10), 5%); @state-info-text: #31708f; @state-info-bg: #d9edf7; @state-info-border: darken(spin(@state-info-bg, -10), 7%); @state-warning-text: #8a6d3b; @state-warning-bg: #fcf8e3; @state-warning-border: darken(spin(@state-warning-bg, -10), 5%); @state-danger-text: #a94442; @state-danger-bg: #f2dede; @state-danger-border: darken(spin(@state-danger-bg, -10), 5%); //== Tooltips // //## //** Tooltip max width @tooltip-max-width: 200px; //** Tooltip text color @tooltip-color: #fff; //** Tooltip background color @tooltip-bg: #000; @tooltip-opacity: .9; //** Tooltip arrow width @tooltip-arrow-width: 5px; //** Tooltip arrow color @tooltip-arrow-color: @tooltip-bg; //== Popovers // //## //** Popover body background color @popover-bg: #fff; //** Popover maximum width @popover-max-width: 276px; //** Popover border color @popover-border-color: rgba(0,0,0,.2); //** Popover fallback border color @popover-fallback-border-color: #ccc; //** Popover title background color @popover-title-bg: darken(@popover-bg, 3%); //** Popover arrow width @popover-arrow-width: 10px; //** Popover arrow color @popover-arrow-color: @popover-bg; //** Popover outer arrow width @popover-arrow-outer-width: (@popover-arrow-width + 1); //** Popover outer arrow color @popover-arrow-outer-color: fadein(@popover-border-color, 5%); //** Popover outer arrow fallback color @popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%); //== Labels // //## //** Default label background color @label-default-bg: @gray-light; //** Primary label background color @label-primary-bg: @brand-primary; //** Success label background color @label-success-bg: @brand-success; //** Info label background color @label-info-bg: @brand-info; //** Warning label background color @label-warning-bg: @brand-warning; //** Danger label background color @label-danger-bg: @brand-danger; //** Default label text color @label-color: #fff; //** Default text color of a linked label @label-link-hover-color: #fff; //== Modals // //## //** Padding applied to the modal body @modal-inner-padding: 15px; //** Padding applied to the modal title @modal-title-padding: 15px; //** Modal title line-height @modal-title-line-height: @line-height-base; //** Background color of modal content area @modal-content-bg: #fff; //** Modal content border color @modal-content-border-color: rgba(0,0,0,.2); //** Modal content border color **for IE8** @modal-content-fallback-border-color: #999; //** Modal backdrop background color @modal-backdrop-bg: #000; //** Modal backdrop opacity @modal-backdrop-opacity: .5; //** Modal header border color @modal-header-border-color: #e5e5e5; //** Modal footer border color @modal-footer-border-color: @modal-header-border-color; @modal-lg: 900px; @modal-md: 600px; @modal-sm: 300px; //== Alerts // //## Define alert colors, border radius, and padding. @alert-padding: 15px; @alert-border-radius: @border-radius-base; @alert-link-font-weight: bold; @alert-success-bg: @state-success-bg; @alert-success-text: @state-success-text; @alert-success-border: @state-success-border; @alert-info-bg: @state-info-bg; @alert-info-text: @state-info-text; @alert-info-border: @state-info-border; @alert-warning-bg: @state-warning-bg; @alert-warning-text: @state-warning-text; @alert-warning-border: @state-warning-border; @alert-danger-bg: @state-danger-bg; @alert-danger-text: @state-danger-text; @alert-danger-border: @state-danger-border; //== Progress bars // //## //** Background color of the whole progress component @progress-bg: #f5f5f5; //** Progress bar text color @progress-bar-color: #fff; //** Variable for setting rounded corners on progress bar. @progress-border-radius: @border-radius-base; //** Default progress bar color @progress-bar-bg: @brand-primary; //** Success progress bar color @progress-bar-success-bg: @brand-success; //** Warning progress bar color @progress-bar-warning-bg: @brand-warning; //** Danger progress bar color @progress-bar-danger-bg: @brand-danger; //** Info progress bar color @progress-bar-info-bg: @brand-info; //== List group // //## //** Background color on `.list-group-item` @list-group-bg: #fff; //** `.list-group-item` border color @list-group-border: #ddd; //** List group border radius @list-group-border-radius: @border-radius-base; //** Background color of single list items on hover @list-group-hover-bg: #f5f5f5; //** Text color of active list items @list-group-active-color: @component-active-color; //** Background color of active list items @list-group-active-bg: @component-active-bg; //** Border color of active list elements @list-group-active-border: @list-group-active-bg; //** Text color for content within active list items @list-group-active-text-color: lighten(@list-group-active-bg, 40%); //** Text color of disabled list items @list-group-disabled-color: @gray-light; //** Background color of disabled list items @list-group-disabled-bg: @gray-lighter; //** Text color for content within disabled list items @list-group-disabled-text-color: @list-group-disabled-color; @list-group-link-color: #555; @list-group-link-hover-color: @list-group-link-color; @list-group-link-heading-color: #333; //== Panels // //## @panel-bg: #fff; @panel-body-padding: 15px; @panel-heading-padding: 10px 15px; @panel-footer-padding: @panel-heading-padding; @panel-border-radius: @border-radius-base; //** Border color for elements within panels @panel-inner-border: #ddd; @panel-footer-bg: #f5f5f5; @panel-default-text: @gray-dark; @panel-default-border: #ddd; @panel-default-heading-bg: #f5f5f5; @panel-primary-text: #fff; @panel-primary-border: @brand-primary; @panel-primary-heading-bg: @brand-primary; @panel-success-text: @state-success-text; @panel-success-border: @state-success-border; @panel-success-heading-bg: @state-success-bg; @panel-info-text: @state-info-text; @panel-info-border: @state-info-border; @panel-info-heading-bg: @state-info-bg; @panel-warning-text: @state-warning-text; @panel-warning-border: @state-warning-border; @panel-warning-heading-bg: @state-warning-bg; @panel-danger-text: @state-danger-text; @panel-danger-border: @state-danger-border; @panel-danger-heading-bg: @state-danger-bg; //== Thumbnails // //## //** Padding around the thumbnail image @thumbnail-padding: 4px; //** Thumbnail background color @thumbnail-bg: @body-bg; //** Thumbnail border color @thumbnail-border: #ddd; //** Thumbnail border radius @thumbnail-border-radius: @border-radius-base; //** Custom text color for thumbnail captions @thumbnail-caption-color: @text-color; //** Padding around the thumbnail caption @thumbnail-caption-padding: 9px; //== Wells // //## @well-bg: #f5f5f5; @well-border: darken(@well-bg, 7%); //== Badges // //## @badge-color: #fff; //** Linked badge text color on hover @badge-link-hover-color: #fff; @badge-bg: @gray-light; //** Badge text color in active nav link @badge-active-color: @link-color; //** Badge background color in active nav link @badge-active-bg: #fff; @badge-font-weight: bold; @badge-line-height: 1; @badge-border-radius: 10px; //== Breadcrumbs // //## @breadcrumb-padding-vertical: 8px; @breadcrumb-padding-horizontal: 15px; //** Breadcrumb background color @breadcrumb-bg: #f5f5f5; //** Breadcrumb text color @breadcrumb-color: #ccc; //** Text color of current page in the breadcrumb @breadcrumb-active-color: @gray-light; //** Textual separator for between breadcrumb elements @breadcrumb-separator: "/"; //== Carousel // //## @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); @carousel-control-color: #fff; @carousel-control-width: 15%; @carousel-control-opacity: .5; @carousel-control-font-size: 20px; @carousel-indicator-active-bg: #fff; @carousel-indicator-border-color: #fff; @carousel-caption-color: #fff; //== Close // //## @close-font-weight: bold; @close-color: #000; @close-text-shadow: 0 1px 0 #fff; //== Code // //## @code-color: #c7254e; @code-bg: #f9f2f4; @kbd-color: #fff; @kbd-bg: #333; @pre-bg: #f5f5f5; @pre-color: @gray-dark; @pre-border-color: #ccc; @pre-scrollable-max-height: 340px; //== Type // //## //** Horizontal offset for forms and lists. @component-offset-horizontal: 180px; //** Text muted color @text-muted: @gray-light; //** Abbreviations and acronyms border color @abbr-border-color: @gray-light; //** Headings small color @headings-small-color: @gray-light; //** Blockquote small color @blockquote-small-color: @gray-light; //** Blockquote font size @blockquote-font-size: (@font-size-base * 1.25); //** Blockquote border color @blockquote-border-color: @gray-lighter; //** Page header border color @page-header-border-color: @gray-lighter; //** Width of horizontal description list titles @dl-horizontal-offset: @component-offset-horizontal; //** Horizontal line color. @hr-border: @gray-lighter; ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-master/wells.less ================================================ // // Wells // -------------------------------------------------- // Base class .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: @well-bg; border: 1px solid @well-border; border-radius: @border-radius-base; .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); blockquote { border-color: #ddd; border-color: rgba(0,0,0,.15); } } // Sizes .well-lg { padding: 24px; border-radius: @border-radius-large; } .well-sm { padding: 9px; border-radius: @border-radius-small; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/bootstrap-overrides.less ================================================ /* * Media - Overriding the Media object to 3.2 version in order to prevent issues like text overflow. */ .media { .clearfix(); & > .pull-left { padding-right: 15px; } & > .pull-right { padding-left: 15px; } overflow: visible; } .media-heading { font-size: 14px; margin-bottom: 10px; } .media-body { zoom: 1; display: block; width: auto; } .media-object { border-radius: 2px; } .close { .opacity(0.5); font-weight: normal; text-shadow: none; &:hover { color: inherit; .opacity(1); } } .dl-horizontal dt { text-align: left; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/breadcrumb.less ================================================ .breadcrumb { border-bottom: 1px solid #E5E5E5; border-radius: 0; & > li { & > a { color: #A9A9A9; &:hover { color: @breadcrumb-active-color; } } } } body { &:not(.sw-toggled) { .breadcrumb { @media (min-width: @screen-sm-min) { padding: 10px 33px 11px; } } } &.sw-toggled { .breadcrumb { @media (min-width: 1199px) { padding: 10px 33px 11px 280px; } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/button.less ================================================ .btn { border: 0; text-transform: uppercase; &[class*="bgm-"]:not(.bgm-white) { color: #fff; } .caret { margin-top: -3px; } &:not(.btn-link) { .z-depth(1); } } .btn-group, .btn-group-vertical { &:not(.bootstrap-select) { box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.3); } .btn, .btn:active, .btn:focus, .btn-group { box-shadow: none !important; } .btn { margin: 0; } } .btn-xs { .button-size(2px; @padding-xs-horizontal; 11px; @line-height-small; @border-radius-small); } .btn-link { color: #797979; text-decoration: none; border-radius: 2px; &:hover { color: #0a0a0a; } &:hover, &:active, &:focus { text-decoration: none; } } .btn-inverse { .button-variant(#fff, #454545, transparent); } .btn-icon { border-radius: 50%; width: 40px; line-height: 42px; height: 40px; padding: 0; text-align: center; .zmdi { font-size: 17px; } } .btn-icon-text { & > .zmdi { font-size: 15px; vertical-align: top; display: inline-block; margin-top: 2px; line-height: 100%; margin-right: 5px; } } .btn-float { width: 50px; height: 50px; border-radius: 50%; line-height: 45px !important; &:not(.m-btn) { position: absolute !important; } i { font-size: 23px; .transition(all); .transition-duration(500ms); } &:hover { i { .rotate(360deg); } } &:not(.bgm-white):not(.bgm-gray) { & > i { color: #fff; } } &:not(.bgm-white):not(.bgm-gray) { & > i { color: #fff; } } &.bgm-white, &.bgm-gray { & > i { color: #333; } } } .open .btn { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; &:focus, &:active { outline: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important; } } /* * Material Design Add button */ .m-btn { z-index: 1; bottom: 40px; right: 40px; position: fixed !important; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/card.less ================================================ .card { position: relative; background: #fff; box-shadow: @card-shadow; margin-bottom: @grid-gutter-width; .card-header { position: relative; &:not(.ch-alt) { @media screen and (min-width: @screen-sm-min) { padding: 23px 25px; } @media screen and (max-width: @screen-sm-max) { padding: 18px; } } h2 { margin: 0; line-height: 100%; font-size: 17px; font-weight: 400; small { display: block; margin-top: 8px; color: #AEAEAE; line-height: 160%; } } &.ch-alt { @media screen and (min-width: @screen-sm-min) { padding: 23px 26px; } @media screen and (max-width: @screen-sm-max) { padding: 18px 18px 28px; } &:not([class*="bgm-"]) { background-color: #f7f7f7; } } &[class*="bgm-"] { h2, h2 small { color: #fff; } } .actions { position: absolute; right: 10px; z-index: 2; top: 15px; } .btn-float { right: 25px; bottom: -23px; z-index: 1; } } .card-body { &.card-padding { @media screen and (min-width: @screen-sm-min) { padding: 23px 26px; } @media screen and (max-width: @screen-sm-max) { padding: 18px; } } &.card-padding-sm { padding: 15px; } } } .card-header:not(.ch-alt):not([class*="bgm-"]) + .card-padding { padding-top: 0; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/chart.less ================================================ .chart-edge { margin: 20px -8px 0 -10px; overflow: hidden; .flot-chart { bottom: -14px; } } .charts-row { margin-top: 50px; margin-bottom: 20px; } .mini-charts-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; .chart { padding: 15px; float: left; &.chart-pie { margin: 0 20px; } } .count { overflow: hidden; color: rgba(255, 255, 255, 0.9); padding: 16px 12px; & > h2 { margin: 0; line-height: 100%; font-size: 22px; font-weight: 300; color: #fff; } & > small { margin-bottom: 2px; display: block; } & > h2, & > small { .text-overflow(); } } & > .clearfix { position: relative; z-index: 1; } &:before { .transition(width); .transition-duration(500ms); .backface-visibility(hidden); content: ""; width: 113px; height: 100%; background: rgba(0,0,0,0.1); position: absolute; left: 0; top: 0; } &:hover { .count { color: #fff !important; } &:before { width: 100%; } } } /* * Sparkline Tooltip */ #jqstooltip { min-width: 21px; min-height: 23px; text-align: center; border: 0; background: #fff; box-shadow: 2px 2px 5px rgba(0,0,0,0.3); background-color: #fff; .jqsfield { font-size: 12px; font-weight: 700; font-family: inherit; text-align: center; color: #333; & > span { display: none; } } } /* * Easy Pie Charts */ .epc-item { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); position: relative; margin-bottom: 30px; padding: 30px 20px; text-align: center; } .easy-pie { display: inline-block; position: relative; padding: 0 5px 10px; .percent { position: absolute; font-weight: 300; width: 100%; line-height: 100%; left: 0; &:after { content: "%"; } } &.main-pie { .percent { margin-top: 49px; font-size: 50px; text-align: center; &:not([class*="c-"]) { color: rgba(255,255,255,0.7); } &:after { font-size: 30px; } } .pie-title { color: #fff; } } &:not(.main-pie) { .percent { font-size: 26px; margin-top: 37px; &:after { font-size: 20px; } } } .pie-title { position: absolute; width: 100%; text-align: center; bottom: -3px; left: 0; } } /* * Recet Items Table Chart */ #recent-items-chart { width: ~"calc(100% + 19px)"; height: 150px; margin: -20px -10px 0; bottom: -10px; } /* * Flot Chart */ [class*="flot-chart"] { width: 100%; display: block; } .flot-chart { height: 200px; } .flot-chart-pie { height: 300px; @media (min-width: @screen-sm-min) { margin-bottom: 20px; } } .flot-tooltip, #flotTip { position: absolute; color: #333; display: none; font-size: 12px; box-shadow: 2px 2px 5px rgba(0,0,0,0.1); padding: 3px 10px; background-color: #fff; z-index: 99999; } [class*="flc-"] { text-align: center; margin: 10px 0 5px; table { display: inline-block; } .legendColorBox { & > div { border: #fff !important; & > div { border-radius: 50%; } } } .legendLabel { padding: 0 8px 0 3px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/chat.less ================================================ #chat { padding: 20px 0; width: @sidebar-right-width; right: -(@sidebar-right-width + 20); &.toggled { right: 0; } .chat-search { padding: 20px 20px 15px 20px; .form-control { .img-retina('../img/icons/search-2.png', '../img/icons/search-2@2x.png', 24px, 24px); background-repeat: no-repeat; background-position: left center; padding-left: 30px; .transition(all); .transition-duration(300ms); &:focus { background-position: right center; padding: 0 30px 0 0; } } } } /* * Chat Status Icons */ [class*="chat-status"] { position: absolute; width: 10px; height: 10px; border-radius: 50%; top: -3px; right: 12px; border: 2px solid #FFF; } /* Simple Mixin */ .chat-status(@color) { box-shadow: 0 0 0 1px @color; background: @color; } .chat-status-online { .chat-status(#1EC01E); } .chat-status-offline { .chat-status(#E73F3F); } .chat-status-busy { .chat-status(#FFA500); } /* * For Stupid IE9 */ .ie9 { #chat { right: 0; &:not(.toggled) { display: none; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/contacts.less ================================================ .contacts { &:not(.c-profile) { padding: 0 8px; } & > [class*="col-"] { padding: 0 10px; } .c-item { border: 1px solid #e2e2e2; border-radius: 2px; margin-bottom: 24px; .ci-avatar { display: block; img { width: 100%; border-radius: 2px 2px 0 0; } } } .ci-avatar { margin: -1px -1px 0; } .c-info { text-align: center; margin-top: 15px; padding: 0 5px; strong { color: #000; font-size: 14px; font-weight: 500; } small { color: #999; margin-top: 3px; } strong, small { .text-overflow(); display: block; } } .c-footer { border-top: 1px solid #e2e2e2; margin-top: 18px; & > button { padding: 4px 10px 3px; color: #333; display: block; width: 100%; text-align: center; color: #333; font-weight: 500; border-radius: 2px; background: #fff; border: 0; & > i { font-size: 16px; vertical-align: middle; margin-top: -3px; } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/dropdown.less ================================================ .dropdown-menu { box-shadow: @dropdown-shadow; .transition(all); .transition-duration(250ms); & > li > a { padding: 8px 17px; .transition(background-color); .transition-duration(300ms); } &.dropdown-menu-lg { width: 300px; } &.dropdown-menu-sm { width: 150px; } &.dropdown-menu-right { right: 0; left: auto; & > li > a { text-align: right; } } &.dm-icon { & > li > a > .zmdi { line-height: 100%; vertical-align: top; font-size: 18px; width: 28px; } } &:not([class*="bgm-"]) { & > li > a { color: #4C4C4C; &:hover { color: #000; } } } &[class*="bgm-"] { & > li > a { font-weight: 300; color: #fff; } } } .dropdown, .btn-group { &:not([data-animation]) { .dropdown-menu { .scale(0); .opacity(0); display: block } } } .dropdown, .bootstrap-select, .btn-group { .dropdown-menu { &:not([data-animation]) { &.pull-right, &.dropdown-menu-right { .transform-origin(top right); } &:not(.pull-right):not(.dropdown-menu-right) { .transform-origin(top left); } } } } .dropup { .dropdown-menu { &:not([data-animation]) { &.pull-right, &.dropdown-menu-right { .transform-origin(bottom right); } &:not(.pull-right):not(.dropdown-menu-right) { .transform-origin(bottom left); } } } } .dropdown, .dropup, .bootstrap-select, .btn-group { &.open { .dropdown-menu { &:not([data-animation]) { .scale(1); .opacity(1); } } } } .dropdown-header { padding: 3px 17px; margin-top: 10px; color: #b1b1b1; text-transform: uppercase; font-weight: normal; } .btn-group.open .dropdown-toggle { box-shadow: none; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/font.less ================================================ /* * Roboto Light */ .font-face(roboto, 'Roboto-Light-webfont', 300, normal); /* * Roboto Regular */ .font-face(roboto, 'Roboto-Regular-webfont', 400, normal); /* * Roboto Medium */ .font-face(roboto, 'Roboto-Medium-webfont', 500, normal); /* * Roboto Bold */ .font-face(roboto, 'Roboto-Bold-webfont', 700, normal); /* * Shadow Light */ .font-face(shadowsintolight, 'shadowsintolight-webfont', 400, normal); ================================================ FILE: material-manage/src/main/webapp/static/less/inc/footer.less ================================================ #footer { position: absolute; bottom: 0; text-align: center; width: 100%; height: @footer-height; color: #a2a2a2; padding-top: 35px; padding-bottom: 15px; .f-menu { display: block; width: 100%; .list-inline(); margin-top: 8px; & > li > a { color: #a2a2a2; &:hover { color: #777; } } } } @media (min-width: @screen-md-max) { body.sw-toggled { #footer { padding-left: @sidebar-left-width; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/form.less ================================================ label { font-weight: 500; } /* * Reset Focus and Active shadows */ input:active, input:focus { outline: 0; box-shadow: none !important; } .form-control { box-shadow: none !important; resize: none; &:active, &:focus { box-shadow: none; } &:not(.fc-alt) { border-left: 0; border-right: 0; border-top: 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; padding: 0; &.auto-size { padding-top: 6px; } } } /* * Checkbox and Radio */ .checkbox, .radio { label { padding-left: 30px; position: relative; } input { top: 0; left: 0; margin-left: 0 !important; z-index: 1; cursor: pointer; .opacity(0); margin-top: 0; } .input-helper { &:before, &:after { .transition(all); .transition-duration(250ms); .backface-visibility(hidden); position: absolute; content: ""; } &:before { left: 0; border: 1px solid #ccc; } } &.disabled { .opacity(0.6); } } .checkbox { input { width: 17px; height: 17px; &:checked + .input-helper { &:before { .scale(0); } &:after { .scale-rotate(1, -50deg); .opacity(1); } } } .input-helper { &:before { top: 0; width: 17px; height: 17px; } &:after { .opacity(0); .scale-rotate(0, 80deg); width: 22px; height: 9px; border-bottom: 2px solid @m-teal; border-left: 2px solid @m-teal; border-bottom-left-radius: 2px; left: -1px; top: 1px; } } } .radio { input { width: 19px; height: 19px; &:checked + .input-helper { &:after { .scale(1); } } } .input-helper { &:before { top: -1px; width: 19px; height: 19px; border-radius: 50%; } &:after { width: 11px; height: 11px; background: @m-teal; border-radius: 50%; top: 3px; left: 4px; .scale(0); } } } .checkbox-inline, .radio-inline { vertical-align: top; margin-top: 0; padding-left: 25px; } /* * Select */ html:not(.ie9) { .select { position: relative; &:before { position: absolute; top: -1px; content: ""; height: ~"calc(100% - 1px)"; width: 30px; background-color: #FFF; background-position: right ~"calc(100% - 7px)"; background-repeat: no-repeat; .img-retina('../img/select.png', '../img/select@2x.png', 12px, 12px); pointer-events: none; z-index: 5; } &:not(.fg-line):before { right: 0; } &.fg-line:before { right: 10px; } } } /* * Input Group Addon */ .input-group { &:not(.input-group-lg):not(.input-group-sm) .input-group-addon { font-size: 15px; } } .input-group-addon { border-width: 0px 0px 1px 0px; min-width: 42px; & > .zmdi { position: relative; top: 3px; } } /* * Input Feilds */ .fg-line { position: relative; vertical-align: top; &:not(.form-group) { display: inline-block; width: 100%; } .form-control { &:disabled { color: #9d9d9d; background: transparent; } } &:not(.disabled):after, &:not(.readonly):after { position: absolute; z-index: 3; bottom: 0; left: 0; height: 2px; width: 0; content: ""; .transition(all); .transition-duration(300ms); } &:not([class*=has-]):after { background: @m-blue; } &.readonly .form-control { color: #9d9d9d; background: transparent; } &.fg-toggled { &:after { width: 100%; } } } .fg-float { margin-top: 2px; position: relative; .form-control { .placeholder(#fff); position: relative; background: transparent; z-index: 1; } .fg-label { .transition(all); .transition-duration(200ms); position: absolute; top: 5px; font-weight: 400; color: #959595; pointer-events: none; z-index: 0; left: 0; white-space: nowrap; } .fg-toggled .fg-label { top: -20px; font-size: 11px; } } .control-label { font-weight: normal; } /* * Toggle Switch */ .ts-color(@color){ input { &:not(:disabled) { &:checked { & + .ts-helper { background: fade(@color, 50%); &:before { background: @color; } &:active { &:before { box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px fade(@color, 20%); } } } } } } } .toggle-switch { display: inline-block; vertical-align: top; .user-select(none); .ts-label { display: inline-block; margin: 0 20px 0 0; vertical-align: top; -webkit-transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); transition: color 0.56s cubic-bezier(0.4, 0, 0.2, 1); } .ts-helper { display: inline-block; position: relative; width: 40px; height: 16px; border-radius: 8px; background: rgba(0,0,0,0.26); -webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); vertical-align: middle; cursor: pointer; &:before { content: ''; position: absolute; top: -4px; left: -4px; width: 24px; height: 24px; background: #fafafa; box-shadow: 0 2px 8px rgba(0,0,0,0.28); border-radius: 50%; webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } } &:not(.disabled) { .ts-helper { &:active { &:before { box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px rgba(128,128,128,0.1); } } } } input { position: absolute; z-index: 1; width: 46px; margin: 0 0 0 -4px; height: 24px; .opacity(0); cursor: pointer; &:checked { & + .ts-helper { &:before { left: 20px; } } } } &:not([data-ts-color]){ .ts-color(@m-teal); } &.disabled { .opacity(0.6); } &[data-ts-color="red"] { .ts-color(@m-red); } &[data-ts-color="blue"] { .ts-color(@m-blue); } &[data-ts-color="amber"] { .ts-color(@m-amber); } &[data-ts-color="purple"] { .ts-color(@m-purple); } &[data-ts-color="pink"] { .ts-color(@m-pink); } &[data-ts-color="lime"] { .ts-color(@m-lime); } &[data-ts-color="cyan"] { .ts-color(@m-cyan); } &[data-ts-color="green"] { .ts-color(@m-green); } } /* * IE 9 Placeholder */ .ie9-placeholder { color: #888 !important; font-weight: normal; } /* * Validation */ .checkbox-fgline-validation(@color) { .checkbox .input-helper { &:before { border-color: lighten(@color, 20%); } &:after { border-bottom-color: lighten(@color, 10%);; border-left-color: lighten(@color, 10%); } } .fg-line:after { background: @color; } } .has-error { .checkbox-fgline-validation(@m-red); } .has-success { .checkbox-fgline-validation(@m-green); } .has-warning { .checkbox-fgline-validation(@m-orange); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/generics.less ================================================ /* * Generate Margin Class * margin, margin-top, margin-bottom, margin-left, margin-right */ .margin (@label, @size: 1, @key:1) when (@size =< 30){ .m-@{key} { margin: @size !important; } .m-t-@{key} { margin-top: @size !important; } .m-b-@{key} { margin-bottom: @size !important; } .m-l-@{key} { margin-left: @size !important; } .m-r-@{key} { margin-right: @size !important; } .margin(@label - 5; @size + 5; @key + 5); } .margin(25, 0px, 0); /* * Generate Padding Class * padding, padding-top, padding-bottom, padding-left, padding-right */ .padding (@label, @size: 1, @key:1) when (@size =< 30){ .p-@{key} { padding: @size !important; } .p-t-@{key} { padding-top: @size !important; } .p-b-@{key} { padding-bottom: @size !important; } .p-l-@{key} { padding-left: @size !important; } .p-r-@{key} { padding-right: @size !important; } .padding(@label - 5; @size + 5; @key + 5); } .padding(25, 0px, 0); /* * Generate Font-Size Classes (8px - 20px) */ .font-size (@label, @size: 8, @key:10) when (@size =< 20){ .f-@{key} { font-size: @size !important; } .font-size(@label - 1; @size + 1; @key + 1); } .font-size(20, 8px, 8); /* * Font Weight */ .f-300 { font-weight: 300 !important; } .f-400 { font-weight: 400 !important; } .f-500 { font-weight: 500 !important; } .f-700 { font-weight: 700 !important; } /* * Position Classes */ .p-relative { position: relative !important; } .p-absolute { position: absolute !important; } .p-fixed { position: fixed !important; } .p-static { position: static !important; } /* * Overflow */ .o-hidden { overflow: hidden !important; } .o-visible { overflow: visible !important; } .o-auto { overflow: auto !important; } /* * Display */ .d-block { display: block !important; } .di-block { display: inline-block !important; } /* * Material Background Colors */ @array: c-white bgm-white @m-white, c-black bgm-black @m-black, c-brown bgm-brown @m-brown, c-pink bgm-pink @m-pink, c-red bgm-red @m-red, c-blue bgm-blue @m-blue, c-purple bgm-purple @m-purple, c-deeppurple bgm-deeppurple @m-deeppurple, c-lightblue bgm-lightblue @m-lightblue, c-cyan bgm-cyan @m-cyan, c-teal bgm-teal @m-teal, c-green bgm-green @m-green, c-lightgreen bgm-lightgreen @m-lightgreen, c-lime bgm-lime @m-lime, c-yellow bgm-yellow @m-yellow, c-amber bgm-amber @m-amber, c-orange bgm-orange @m-orange, c-deeporange bgm-deeporange @m-deeporange, c-gray bgm-gray @m-gray, c-bluegray bgm-bluegray @m-bluegray, c-indigo bgm-indigo @m-indigo; .for(@array); .-each(@value) { @name: extract(@value, 1); @name2: extract(@value, 2); @color: extract(@value, 3); &.@{name2} { background-color: @color !important; } &.@{name} { color: @color !important; } } /* * Background Colors */ .bg-black-trp { background-color: rgba(0,0,0,0.1) !important; } /* * Border */ .b-0 { border: 0 !important; } /* * width */ .w-100 { width: 100% !important; } /* * Border Radius */ .brd-2 { border-radius: 2px; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/header.less ================================================ /* * Common header classes & IDs * Do not remove this */ .header-inner { list-style: none; padding: 17px 0; margin-bottom: 0; position: relative; & > li { &:not(.pull-right) { float: left; } &:not(:last-child) { margin-right: -2px; } } } .logo { a { color: #fff; text-transform: uppercase; display: block; font-size: 16px; } } #menu-trigger { width: 65px; height: 35px; cursor: pointer; .line-wrap .line { background-color: #fff; } &:before { content: ""; position: absolute; top: 13px; left: 7px; width: 45px; height: 45px; border-radius: 50%; background: rgba(255, 255, 255, 0.22); .transition(all); .transition-duration(300ms); .scale(0); z-index: 0; } &.open { &:before { .scale(1); } } } .top-menu { list-style: none; padding: 0; & > li { display: inline-block; margin: 0 1px; vertical-align: top; min-width: 50px; @media (max-width: @screen-xs-max) { position: static !important; } .dropdown-menu-lg { padding: 0; .lv-body { min-height: 295px; overflow-x: hidden; } } &:not(#toggle-width) { .hover-pop(rgba(0,0,0,0.12), 2px, 250ms, 0); } & > a { color: #fff; display: block; text-align: center; z-index: 1; position: relative; & > .tm-icon { font-size: 24px; line-height: 36px; } & > .tm-label { line-height: 35px; white-space: nowrap; padding: 0 10px; font-size: @font-size-base + 1; text-transform: uppercase; } & > .tmn-counts { position: absolute; font-style: normal; background: @m-red; padding: 1px 5px; border-radius: 2px; right: 7px; top: -3px; font-size: 10px; line-height: 15px; } } } @media (max-width: @screen-xs-max) { .dropdown-menu-lg { width: ~"calc(100% - 28px)" !important; } .dropdown-menu { right: 14px; top: 55px; } } } #notifications { position: relative; .lv-body { overflow-x: hidden; } &:before { content: ""; position: absolute; width: 100%; height: ~"calc(100% - 70px)"; background: url(../img/notifications.png) no-repeat center; .transition(all); .transition-duration(400ms); .scale-rotate(0, -180deg); .opacity(0); top: 42px; } &.empty:before { .scale-rotate(1, 0deg); .opacity(1); } } /* Full Screen */ :-webkit-full-screen [data-action="fullscreen"] { display: none; } :-moz-full-screen [data-action="fullscreen"] { display: none; } :-ms-fullscreen [data-action="fullscreen"] { display: none; } :full-screen [data-action="fullscreen"] { display: none; } :fullscreen [data-action="fullscreen"] { display: none; } /* ----------------------------- End common header classes and IDs------------------------------------- */ /* * For header type 1 only * You may remove these if you opt header 2 */ #header { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); min-height: @header-height; .user-select(none); &:not(.sidebar-toggled) { &.header-up { .translate3d(0, -70px, 0); } } position: fixed; z-index: 11; width: 100%; left: 0; top: 0; padding: 0 11px; .logo a { padding: 7px 10px; } } #top-search-wrap { position: absolute; top: -65px; left: 0; width: 100%; height: @header-height; background: #fff; .transition(all); .transition-duration(300ms); .opacity(0); z-index: 10; input[type="text"] { border: 0; height: 40px; padding: 0 10px 0 55px; font-size: 18px; width: 500px; border-radius: 2px; background-color: darken(@ace, 3%); width: 100%; } #top-search-close { position: absolute; top: 15px; font-size: 23px; font-style: normal; width: 45px; text-align: center; border-radius: 2px 0px 0px 2px; cursor: pointer; left: 15px; height: 40px; padding-top: 9px; &:hover { background-color: darken(@ace, 8%); } @media (max-width: @screen-xs-max) { right: 7px; } } } .tsw-inner { position: relative; padding: 15px; max-width: 700px; display: block; margin: 0 auto; } &.search-toggled { #top-search-wrap { top: 0; .opacity(1); } } /* Full Width Layout */ #toggle-width { @media(min-width: @screen-lg-min) { .toggle-switch { margin: 9px 30px 0 0; .ts-helper { height: 11px; width: 33px; &:before { width: 20px; height: 20px; top: -5px; } } input:checked + .ts-helper { background: rgba(0, 0, 0, 0.26); &:before { left: 18px; background: #fff; } } } } @media(max-width: @screen-lg-min) { display: none; } } .sw-toggled { @media (min-width: @screen-lg-min) { #header { padding-left: 15px; } #menu-trigger { display: none; } } } /* For Stupid IE9 */ .ie9 { #header:not(.sidebar-toggled) { &.header-up { display: none; } } } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For Header type 2 only * You may remove these if you opt header 1 */ #header-2 { box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); position: relative; margin-bottom: -@header-height; z-index: 10; @media (min-width: @screen-sm-min) { padding: 15px 30px 0; &:before { content: ""; position: absolute; bottom: 0; left: 0; background: rgba(0,0,0,0.04); width: 100%; height: 49px; } } .search { margin-bottom: 25px; @media (max-width: @screen-xs-max) { padding: 0 20px } input[type="text"] { width: 100%; background: transparent; border: 0; border-bottom: 1px solid rgba(255, 255, 255, 0.24); color: #fff; font-size: 15px; font-weight: 300; padding: 6px 0 6px 30px; .placeholder(#fff); } &:after { background: @m-yellow; } .fg-line { max-width: 500px; position: relative; &:after { background: @m-yellow; } &:before { content: '\f1c3'; font-family: @font-icon-md; position: absolute; left: 0; bottom: 1px; color: #fff; font-size: 22px; } } } } .ha-menu { & > ul { list-style: none; padding: 0; margin: 0; & > li { display: inline-block; vertical-align: top; @media (max-width: @screen-xs-max) { display: block; } &:not(.active) > *:not(ul) { color: rgba(255,255,255,0.6); } &.active > *:not(ul) { color: #fff; box-shadow: inset 0px -3px 0 0px @m-yellow; @media (max-width: @screen-xs-max) { display: block; } } & > *:not(ul) { text-transform: uppercase; padding: 15px 12px; display: block; } &.open > *:not(ul), & > *:not(ul):hover { color: #fff; } .dropdown-menu { margin-top: -5px; min-width: 100%; } } } @media (max-width: @screen-xs-max) { width: 200px; position: absolute; top: 65px; left: 8px; box-shadow: 0 0 10px; z-index: 10; padding: 0 10px; &:not(.toggled) { display: none; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/ie-warning.less ================================================ .ie-warning { position: fixed; top: 0; left: 0; z-index: 9999; background: @m-black; width: 100%; height: 100%; text-align: center; color: #fff; font-family: "Courier New", Courier, monospace; padding: 50px 0; p { font-size: 17px; } .iew-container { min-width: 1024px; width: 100%; height: 200px; background: #fff; margin: 50px 0; } .iew-download { list-style: none; padding: 30px 0; margin: 0 auto; width: 720px; & > li { float: left; vertical-align: top; & > a { display: block; color: #000; width: 140px; font-size: 15px; padding: 15px 0; & > div { margin-top: 10px; } &:hover { background-color: #eee; } } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/invoice.less ================================================ .invoice { min-width: 1100px; max-width: 1170px; } .i-logo { width: 150px; } .i-table { .highlight { background-color: #eee; border-bottom: 1px solid darken(#eee, 3%); } td.highlight { font-size: 14px; font-weight: 500; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/less-plugins/for.less ================================================ .for(@i, @n) {.-each(@i)} .for(@n) when (isnumber(@n)) {.for(1, @n)} .for(@i, @n) when not (@i = @n) { .for((@i + (@n - @i) / abs(@n - @i)), @n); } .for(@array) when (default()) {.for-impl_(length(@array))} .for-impl_(@i) when (@i > 1) {.for-impl_((@i - 1))} .for-impl_(@i) when (@i > 0) {.-each(extract(@array, @i))} ================================================ FILE: material-manage/src/main/webapp/static/less/inc/list.less ================================================ .clist { list-style: none; & > li { &:before { font-family: @font-icon-md; margin: 0 10px 0 -20px; vertical-align: middle; } } &.clist-angle > li:before { content: "\f2fb"; } &.clist-check > li:before { content: "\f26b"; } &.clist-star > li:before { content: "\f27d"; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/listview.less ================================================ .listview { position: relative; &:not(.lv-lg):not(.lv-message) { .lv-item { padding: 10px 20px; } } &.lv-lg { .lv-item { @media (min-width: @screen-xs-min) { padding: 17px 35px 17px 25px; } @media (max-width: @screen-xs-max) { padding: 17px 35px 17px 20px; } &:hover { background-color: #FFFFDB; } } } .lv-item { position: relative; display: block; .transition(background-color); .transition-duration(300ms); .lv-small { font-size: 12px; color: #A9A9A9; .text-overflow(); display: block; width: 100%; } .checkbox, &.media { margin: 0; } .lv-actions { position: absolute; right: 15px; top: 10px; @media (max-width: @screen-xs-min) { right: 7px; } } } .lv-title { .text-overflow(); display: block; } a.lv-item:hover { background: #ECF9FF; } [class*="lv-img"] { border-radius: 50%; } .lv-img { width: 48px; height: 48px; } .lv-img-sm { width: 35px; height: 35px; } &.lv-bordered { .lv-item { &:not(:last-child) { border-bottom: 1px solid #f0f0f0; } } } .lv-attrs { list-style: none; padding: 0; margin: 5px 0 0 0; .listview-attrs(@b-color: lighten(@text-color, 50%), @bg: #fff, @color: @text-color) { border: 1px solid @b-color; background: @bg; color: @color; } & > li { display: inline-block; padding: 2px 10px 3px; font-size: 12px; margin-top: 5px; margin-right: 2px; &:not(.info):not(.primary):not(.warning):not(.danger) { .listview-attrs(); } &.info { .listview-attrs(@m-cyan, @m-cyan, #fff); } &.primary { .listview-attrs(@m-blue, @m-blue, #fff); } &.warning { .listview-attrs(@m-orange, @m-orange, #fff); } &.danger { .listview-attrs(@m-red, @m-red, #fff); } & > a { display: block; } } } &:not(.lv-message) { .lv-title { color: #000; } } } [class*="lv-img"] { border-radius: 50%; } .lv-img { width: 48px; height: 48px; } .lv-img-sm { width: 35px; height: 35px; } .lv-header { text-align: center; padding: 15px 10px 13px; line-height: 100%; text-transform: uppercase; border-bottom: 1px solid #F0F0F0; font-weight: 500; color: #4C4C4C; margin-bottom: 10px; .actions { position: absolute; top: 6px; right: 8px; z-index: 10; } } .lvh-search { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 4; background: #fff; display: none; &:before { content: "\f1c3"; font-family: 'Material-Design-Iconic-Font'; position: absolute; left: 24px; top: 17px; font-size: 22px; } } .lvhs-input { border: 0; padding: 0 26px 0 55px; height: 63px; font-size: 18px; width: 100%; font-weight: 100; background: #fff; border-bottom: 1px solid #EEE; } .lvh-search-close { font-style: normal; position: absolute; top: 23px; right: 22px; font-size: 17px; width: 18px; height: 18px; background-color: #ADADAD; line-height: 100%; color: #fff; text-align: center; cursor: pointer; border-radius: 50%; &:hover { background: #333; } } .lv-header-alt { position: relative; background: #f8f8f8; padding: 15px; .lv-actions { z-index: 3; float: right; margin-top: 3px; position: relative; & > li > a { margin: 0 3px; } } } .lvh-label { color: #818181; display: inline-block; margin: 0; font-size: 14px; font-weight: normal; padding: 0 6px; line-height: 33px; vertical-align: middle; float: left; } .lv-footer { display: block; text-align: center; padding: 7px 10px 8px; border-top: 1px solid #F0F0F0; line-height: 100%; font-size: 11px; margin-top: 20px; color: #828282; } a.lv-footer:hover { color: darken(#919191, 55%); } /* * Inside Card will have more padding */ .card-body { .lv-item { padding: 12px 20px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/login.less ================================================ .login-content { overflow: hidden; height: 100%; } .lc-block { background: #fff; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); border-radius: 2px; width: 500px; display: inline-block; margin-top: 42px; vertical-align: middle; position: relative; &:not(.toggled) { display: none; } &.toggled { .animated(fadeInUp, 300ms); z-index: 10; } &:not(.lcb-alt) { padding: 35px 55px 35px; } @media (max-width: @screen-xs-max) { padding: 15px 35px 25px 20px; width: ~"calc(100% - 60px)"; } .checkbox { margin: 5px 0 0 42px; text-align: left; } &:not(.lcb-alt) { .btn-login { top: 50%; margin-top: -25px; right: -25px; } } } .login-navigation { list-style: none; padding: 0; margin: 0; position: absolute; width: 100%; text-align: center; left: 0%; bottom: -45px; & > li { display: inline-block; margin: 0 2px; .transition(all); .transition-duration(150ms); cursor: pointer; vertical-align: top; color: #fff; line-height: 16px; min-width: 16px; min-height: 16px; text-transform: uppercase; .backface-visibility(hidden); & > span { .opacity(0); } &:not(:hover) { font-size: 0px; border-radius: 100%; } &:hover { border-radius: 10px; padding: 0 5px; font-size: 8px; & > span { .opacity(1); } } } } .lcb-alt { padding: 70px 55px 60px; .btn-login { bottom: -25px; left: 50%; margin-left: -25px; } .login-navigation { bottom: -75px; } } .lcb-user { width: 100px; height: 100px; border-radius: 50%; border: 5px solid #fff; position: absolute; top: -50px; left: 50%; margin-left: -50px; box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.18); } body.login-content { text-align: center; &:after { content: ""; vertical-align: middle; display: inline-block; width: 1px; height: 100%; } &:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: @m-cyan; content: ""; z-index: 0; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/media.less ================================================ .thumbnail { a&:hover, a&:focus, a&.active { border-color: darken(@thumbnail-border, 5%); box-shadow: 0 0 6px #EAEAEA; } } /* * Lightbox */ .lightbox { .lightbox-item { & > img { width: 100%; border-radius: 2px; } .hover-pop(rgba(0,0,0,0.1)); &:hover { cursor: pointer; } } [data-src] { .clearfix(); } .lightbox-item:not(.p-item) { position: relative; } } /* * Carousel */ .carousel { .carousel-control { .transition(all); .transition-duration(250ms); .opacity(0); .zmdi { position: absolute; top: 50%; left: 50%; line-height: 100%; @media screen and (min-width: @screen-sm-min) { font-size: 60px; width: 60px; height: 60px; margin-top: -30px; margin-left: -30px; } @media screen and (max-width: @screen-sm-max) { width: 24px; height: 24px; margin-top: -12px; margin-left: -12px; } } } &:hover { .carousel-control { .opacity(1); } } .carousel-caption { background: rgba(0,0,0,0.6); left: 0; right: 0; bottom: 0; width: 100%; padding-bottom: 50px; & > h3 { color: #fff; margin: 0 0 5px; font-weight: 300; } & > p { margin: 0; } @media screen and (max-width: @screen-sm-max) { display: none; } } .carousel-indicators { bottom: 10px; margin: 0; left: 0; bottom: 0; width: 100%; padding: 0 0 6px; background: rgba(0,0,0,0.6); li { border-radius: 0; width: 15px; border: 0; background: #fff; height: 3px; margin: 0; .transition(all); .transition-duration(250ms); &.active { width: 25px; height: 3px; background: @m-orange; } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/messages.less ================================================ #messages-main { position: relative; .clearfix(); .ms-block { padding: 23px 20px 0; } .ms-menu { position: absolute; left: 0; top: 0; background: #F8F8F8; border-right: 1px solid #EEE; padding-bottom: 50px; height: 100%; width: 240px; .transition(all); .transition-duration(250ms); @media (max-width: @screen-xs-max) { height: ~"calc(100% - 58px)"; .translate3d(-240px, 58px, 0); .opacity(0); z-index: 1; &.toggled { .translate3d(0, 58px, 0); .opacity(1); } } .lv-item { padding-left: 20px; padding-right: 20px; &.active { background: #fff; } &:not(.active):hover { background: #F2F2F2; cursor: pointer; } } } .ms-body { @media (min-width: @screen-sm-min) { padding-left: 240px; } @media (max-width: @screen-xs-max) { overflow: hidden; } } .ms-user { .clearfix(); & > img { border-radius: 50%; width: 40px; float: left; } & > div { overflow: hidden; padding: 7px 5px 7px 15px; font-size: 11px; } } } #ms-menu-trigger { .user-select(none); float: left; margin: 1px 0 0 -7px; @media (min-width: @screen-sm-min) { display: none; } .line-wrap .line { background-color: #717171; } } /* * For Message */ .lv-message { .lv-item { padding: 20px; &.right { text-align: right; .lv-avatar { margin-right: 0; margin-left: 15px; } } &:not(.right) { .ms-item { background: @m-amber; color: #fff; } } &.right .ms-item { background: #eee; } } } .lv-avatar { width: 35px; height: 35px; border-radius: 50%; color: #FFF; text-align: center; line-height: 34px; font-size: 15px; margin-right: 15px; padding: 0 !important; text-transform: uppercase; & > img { width: 35px; height: 35px; border-radius: 50%; vertical-align: top; } } .ms-item { padding: 13px 19px 15px; border-radius: 2px; display: inline-block; @media (min-width: @screen-sm-min) { max-width: 70%; } } .ms-date { display: block; color: #B3B3B3; margin-top: 7px; & > i { font-size: 14px; vertical-align: bottom; line-height: 100%; } } .ms-reply { box-shadow: 0 -20px 20px -5px #FFF; position: relative; margin: 0 !important; textarea { width: 100%; font-size: 13px; border: 0; padding: 10px 8px; resize: none; height: 60px; } button { position: absolute; top: 0; right: 0; border: 0; height: 100%; width: 60px; font-size: 25px; background: #F5F5F5; color: @m-blue; &:hover { background: #f2f2f2; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/misc.less ================================================ /* * Block Header * Used for Heading outside the Cards. */ .block-header { @media screen and (min-width: @screen-sm-min) { padding: 0 22px; } @media screen and (max-width: @screen-sm-max) { padding: 0 18px; } margin-bottom: 25px; position: relative; & > h2 { font-size: 15px; color: #777; margin: 0; font-weight: 400; text-transform: uppercase; & > small { display: block; text-transform: none; margin-top: 8px; margin-bottom: 20px; color: #9E9E9E; line-height: 140%; } } .actions { position: absolute; right: 10px; top: -5px; z-index: 4; } } /* * Header Actions */ .actions { list-style: none; padding: 0; z-index: 3; margin: 0; & > li { display: inline-block; vertical-align: baseline; } & > li > a, & > a { width: 30px; height: 30px; display: inline-block; text-align: center; padding-top: 5px; & > i { color: #adadad; font-size: 20px; } &:hover { & > i { color: #000; } } .hover-pop(rgba(0,0,0,0.1), 50%, 250ms, 0); } & > li.open, &.open { & > a { & > i { color: #000; } &:before { .scale(1); .opacity(1); } } } &.actions-alt { & > li { & > a > i { color: #fff; &:hover { color: #fff; } } &.open { & > a { & > i { color: #fff; } } } } } &.open { z-index: 3; } } /* * Collapse Menu Icons */ .line-wrap { width: 18px; height: 12px; .transition(all); .transition-duration(300ms); margin: 12px 20px; .line{ width: 18px; height: 2px; .transition(all); .transition-duration(300ms); &.center { margin: 3px 0; } } } &.open { .line-wrap { .rotate(180deg); .line { &.top { width: 12px; transform: translateX(8px) translateY(1px) rotate(45deg); -webkit-transform: translateX(8px) translateY(1px) rotate(45deg); } &.bottom { width: 12px; transform: translateX(8px) translateY(-1px) rotate(-45deg); -webkit-transform: translateX(8px) translateY(-1px) rotate(-45deg); } } } } /* * Load More */ .load-more { text-align: center; margin-top: 30px; a { padding: 5px 10px 3px; display: inline-block; background-color: @m-red; color: #FFF; border-radius: 2px; white-space: nowrap; i { font-size: 20px; vertical-align: middle; position: relative; margin-top: -2px; } &:hover { background-color: darken(@m-red, 10%); } } } /* * Page Loader */ html { &:not(.ismobile) { .page-loader { background: #fff; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; .preloader { width: 50px; position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -55px; .animated(fadeIn, 3000ms); p { white-space: nowrap; position: relative; left: -9px; top: 22px; color: #CCC; } } } } &.ismobile .page-loader { display: none; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/mixin.less ================================================ /* * Font Face */ .font-face(@family, @name, @weight: 300, @style){ @font-face{ font-family: @family; src:url('../fonts/@{family}/@{name}.eot'); src:url('../fonts/@{family}/@{name}.eot?#iefix') format('embedded-opentype'), url('../fonts/@{family}/@{name}.woff') format('woff'), url('../fonts/@{family}/@{name}.ttf') format('truetype'), url('../fonts/@{family}/@{name}.svg#icon') format('svg'); font-weight: @weight; font-style: @style; } } /* * Background Repeat + Position */ .bg-option(@repeat: no-repeat, @position: center) { background-repeat: @repeat; background-position: @position; } /* * CSS Animations based on animate.css */ .animated(@name, @duration) { -webkit-animation-name: @name; animation-name: @name; -webkit-animation-duration: @duration; animation-duration: @duration; -webkit-animation-fill-mode: both; animation-fill-mode: both; } /* * CSS Transform - Scale and Rotate */ .scale-rotate(@scale, @rotate) { -webkit-transform: scale(@scale) rotate(@rotate); -ms-transform: scale(@scale) rotate(@rotate); -o-transform: scale(@scale) rotate(@rotate); transform: scale(@scale) rotate(@rotate); } /* * User Select */ .user-select (@val) { -webkit-touch-callout: @val; -webkit-user-select: @val; -khtml-user-select: @val; -moz-user-select: @val; -ms-user-select: @val; user-select: @val; } /* * Background Image Cover */ .bg-cover(@image) { background-image: url(@image); background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } .bg-cover-inline() { background-repeat: no-repeat; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background-position: center; } /* * Tab Focus */ .tab-focus() { outline: none !important; } /* * Pop-in Hover effects */ .hover-pop(@background: ~"rgba(0,0,0,0.5)", @radius: 0, @duration: 250ms, @zindex: 0) { @media (min-width: @screen-sm-min) { position: relative; &:before { left: 0; top: 0; content: ""; position: absolute; width: 100%; height: 100%; .scale3d(0,0,0); .transition(all); .transition-duration(@duration); .backface-visibility(hidden); background-color: @background; z-index: @zindex; border-radius: @radius; .opacity(0); } &:hover:before, &.open:before { .scale3d(1,1,1); .opacity(1); } } } /* * Override Bootstrap Button Mixin */ .button-variant(@color; @background; @border) { color: @color; background-color: @background; border-color: @border; &:hover, &:focus, &.focus, &:active, .open > .dropdown-toggle& { color: @color; background-color: @background; border-color: transparent; &:hover, &:focus, &.focus { color: @color; background-color: @background; border-color: transparent } } &:active, &.active, .open > .dropdown-toggle& { background-image: none; } &.disabled, &[disabled], fieldset[disabled] & { &, &:hover, &:focus, &.focus, &:active { background-color: @background; border-color: @border; } } .badge { color: @background; background-color: @color; } } /* * Scale 3d */ .scale3d(...) { @process: ~`(function(e){return e=e||"1, 1, 1"})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`; -webkit-transform: scale3d(@process); -moz-transform: scale3d(@process); -ms-transform: scale3d(@process); -o-transform: scale3d(@process); transform: scale3d(@process); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/modal.less ================================================ .modal { .modal-content { box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31); border-radius: 3px; border: 0; } .modal-header { padding: 23px 26px; } .modal-body { padding: 0 26px 10px; } .modal-footer { .btn-link { font-size: 14px; color: #000; font-weight: 500; &:hover { background-color: #eee; } } } &:not([data-modal-color]) { .modal-footer { .btn-link { font-weight: 500; &:hover { background-color: #eee; } } } } &[data-modal-color] { color: #fff; .modal-title, .modal-footer .btn-link { color: #fff; } .modal-footer { background: rgba(0,0,0,0.1); } .modal-backdrop { background: #fff; } .modal-footer { .btn-link { font-weight: 400; &:hover { background-color: rgba(0,0,0,0.1); } } } } &[data-modal-color="blue"] .modal-content { background: @m-blue; } &[data-modal-color="cyan"] .modal-content { background: @m-cyan; } &[data-modal-color="green"] .modal-content { background: @m-green; } &[data-modal-color="lightgreen"] .modal-content { background: @m-lightgreen; } &[data-modal-color="lightblue"] .modal-content { background: @m-lightblue; } &[data-modal-color="amber"] .modal-content { background: @m-amber; } &[data-modal-color="teal"] .modal-content { background: @m-teal; } &[data-modal-color="orange"] .modal-content { background: @m-orange; } &[data-modal-color="bluegray"] .modal-content { background: @m-bluegray; } &[data-modal-color="red"] .modal-content { background: @m-red; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/pagination.less ================================================ .pagination { border-radius: 0; & > li { margin: 0 2px; display: inline-block; vertical-align: top; & > a, & > span { border-radius: 50% !important; padding: 0; width: 40px; height: 40px; line-height: 38px; text-align: center; font-size: 14px; z-index: 1; position: relative; cursor: pointer; & > .zmdi { font-size: 22px; line-height: 39px; } } &.disabled { .opacity(0.5); } } } /* * Listview Pagination */ .lv-pagination { width: 100%; text-align: center; padding: 40px 0; border-top: 1px solid #F0F0F0; margin-top: 0; margin-bottom: 0; } /* * Pager */ .pager li > a, .pager li > span { padding: 5px 10px 6px; color: @pagination-color; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/panel.less ================================================ .panel { box-shadow: none; border: 0; } .panel-heading { padding: 0; } .panel-title { & > a { padding: 10px 15px; display: block; font-size: 13px; } } .panel-collapse { .panel-heading { position: relative; .panel-title { & > a { padding: 8px 5px 16px 30px; color: #000; position: relative; &:after, &:before { position: absolute; bottom: 0; left: 0; height: 2px; width: 100%; content: ""; .transition(all); .transition-duration(300ms); .backface-visibility(hidden); } &:after { .scale(0); } } } &:not(.active) .panel-title > a { &:before { background: #eee; } } &:before, &:after { font-family: @font-icon-md; font-size: 17px; position: absolute; left: 0; .transition(all); .transition-duration(300ms); .backface-visibility(hidden); top: 4px; } &:before { content: "\f278"; .scale(1); } &:after { .scale(0); content: "\f273"; } &.active { .panel-title > a { &:after { .scale(1); } } &:before { .scale-rotate(0, -90deg); } &:after { .scale(1); } } } .panel-body { border-top: 0 !important; padding-left: 5px; padding-right: 5px; } } .panel-collapse-color(@color) { .panel-collapse { .panel-heading { &.active .panel-title > a { &:after { background: @color; } } } } } .panel-group { &:not([data-collapse-color]) { .panel-collapse-color(@m-blue); } &[data-collapse-color="red"] { .panel-collapse-color(@m-red); } &[data-collapse-color="green"] { .panel-collapse-color(@m-green); } &[data-collapse-color="amber"] { .panel-collapse-color(@m-amber); } &[data-collapse-color="teal"] { .panel-collapse-color(@m-teal); } &[data-collapse-color="black"] { .panel-collapse-color(@m-black); } &[data-collapse-color="cyan"] { .panel-collapse-color(@m-cyan); } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/photos.less ================================================ .photos { margin: 2px 0 0; .lightbox { margin: 0 -8px; } &:not(.p-timeline) { [data-src] { padding: 3px; .transition(all); .transition-duration(150ms); } } } .p-timeline { position: relative; padding-left: 80px; margin-bottom: 75px; [data-src] { float: left; width: 70px; height: 70px; margin: 0 3px 3px 0; } &:last-child .pt-line:before { height: 100%; } } .ptb-title { font-size: 15px; font-weight: 400; margin-bottom: 20px; } .pt-line { position: absolute; left: 0; top: 0; height: 100%; line-height: 14px; &:before, &:after { content: ""; position: absolute; } &:before { width: 1px; height: ~"calc(100% + 63px)"; background: #E2E2E2; top: 14px; right: -20px; } &:after { top: 2px; right: -26px; width: 13px; height: 13px; border: 1px solid #C1C1C1; border-radius: 50%; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/popover.less ================================================ .popover { box-shadow: 0 2px 30px rgba(0, 0, 0, 0.2); } .popover-title { border-bottom: 0; padding: 15px; font-size: 12px; text-transform: uppercase; & + .popover-content { padding-top: 0; } } .popover-content { padding: 15px; p { margin-bottom: 0; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/preloader.less ================================================ .preloader { position: relative; margin: 0px auto; display: inline-block; &:not([class*="pl-"]) { width: 40px; } &:before { content:''; display: block; padding-top: 100%; } &.pl-xs { width: 20px; } &.pl-sm { width: 30px; } &.pl-lg { width: 50px; } &.pl-xl { width: 80px; } &.pl-xxl { width: 100px; } &:not([class*="pls-"]) { .plc-path { animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite; } } &[class*="pls-"] .plc-path { animation: dash 1.5s ease-in-out infinite; } &.pls-red .plc-path { stroke: @m-red; } &.pls-blue .plc-path { stroke: @m-blue; } &.pls-green .plc-path { stroke: @m-green; } &.pls-yellow .plc-path { stroke: @m-yellow; } &.pls-bluegray .plc-path { stroke: @m-bluegray; } &.pls-amber .plc-path { stroke: @m-amber; } &.pls-teal .plc-path { stroke: @m-teal; } &.pls-gray .plc-path { stroke: @m-gray; } &.pls-pink .plc-path { stroke: @m-pink; } &.pls-purple .plc-path { stroke: @m-purple; } &.pls-white .plc-path { stroke: #fff; } } .pl-circular { animation: rotate 2s linear infinite; height: 100%; transform-origin: center center; width: 100%; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; } .plc-path { stroke-dasharray: 1,200; stroke-dashoffset: 0; stroke-linecap: round; stroke-width: 2; stroke-miterlimit: 10; fill: none; } @keyframes rotate{ 100%{ transform: rotate(360deg); } } @keyframes dash{ 0%{ stroke-dasharray: 1,200; stroke-dashoffset: 0; } 50%{ stroke-dasharray: 89,200; stroke-dashoffset: -35px; } 100%{ stroke-dasharray: 89,200; stroke-dashoffset: -124px; } } @keyframes color{ 100%, 0%{ stroke: @m-red; } 40%{ stroke: @m-blue; } 66%{ stroke: @m-green; } 80%, 90%{ stroke: @m-amber; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/pricing-table.less ================================================ .pt-inner { text-align: center; .pti-header { padding: 45px 10px 70px; color: #fff; position: relative; margin-bottom: 15px; & > h2 { margin: 0; line-height: 100%; color: #fff; font-weight: 100; font-size: 50px; small { color: #fff; letter-spacing: 0; vertical-align: top; font-size: 16px; font-weight: 100; } } .ptih-title { background-color: rgba(0, 0, 0, 0.1); padding: 8px 10px 9px; text-transform: uppercase; margin: 0 -10px; position: absolute; width: 100%; bottom: 0; } } .pti-body { padding: 0 23px; .ptib-item { padding: 15px 0; font-weight: 400; &:not(:last-child) { border-bottom: 1px solid #eee; } } } .pti-footer { padding: 10px 20px 30px; & > a { width: 60px; height: 60px; border-radius: 50%; text-align: center; color: #fff; display: inline-block; line-height: 60px; font-size: 30px; .transition(all); .transition-duration(300ms); &:hover { .opacity(0.85); .z-depth(1); } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/print.less ================================================ @media print { @page { margin: 0; size: auto; } body { margin: 0mm 0mm 0mm 0mm !important; padding: 0mm !important; } #header, #footer, #sidebar, #chat, .growl-animated, .m-btn { display: none !important; } /* * INVOICE */ .invoice { padding: 30px !important; -webkit-print-color-adjust: exact !important; .card-header { background: #eee !important; padding: 20px; margin-bottom: 20px; margin: -60px -30px 25px -30px; } .block-header { display: none; } .highlight { background: #eee !important; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/profile.less ================================================ #profile-main { min-height: 500px; position: relative; .pm-overview { overflow-y: auto; @media (min-width: 1200px) { width: 300px; } @media (min-width: @screen-sm-min) and (max-width: 1200px) { width: 250px; } @media (min-width: @screen-sm-min) { position: absolute; left: 0; top: 0; height: 100%; background: #f8f8f8; border-right: 1px solid #eee; } @media (max-width: @screen-xs-max) { width: 100%; background: #333; text-align: center; } } .pm-body { @media (min-width: 1200px) { padding-left: 300px; } @media (min-width: @screen-sm-min) and (max-width: 1200px) { padding-left: 250px; } @media (max-width: @screen-xs-max) { padding-left: 0; } } .pmo-pic { position: relative; margin: 20px; img { @media(min-width: @screen-sm-min) { width: 100%; border-radius: 2px 2px 0 0; } @media(max-width: @screen-xs-max) { width: 180px; display: inline-block; height: 180px; border-radius: 50%; border: 4px solid #fff; .z-depth(2); } } .pmo-stat { border-radius: 0 0 2px 2px; color: #fff; text-align: center; padding: 30px 5px 0; @media(min-width: @screen-sm-min) { background: @m-amber; padding-bottom: 15px; } } .pmop-edit { position: absolute; top: 0; left: 0; color: #fff; background: rgba(0, 0, 0, 0.38); text-align: center; padding: 10px 10px 11px; .transition(opacity); .transition-duration(250ms); &:hover { background: rgba(0, 0, 0, 0.8); } i { font-size: 18px; vertical-align: middle; margin-top: -3px; } @media (min-width: @screen-sm-min) { width: 100%; .opacity(0); i { margin-right: 4px; } } } &:hover { .pmop-edit { .opacity(1); } } .pmop-message { position: absolute; bottom: 27px; left: 50%; margin-left: -25px; .dropdown-menu { padding: 5px 0 55px; left: -90px; width: 228px; height: 150px; top: -74px; .transform-origin(center); textarea { width: 100%; height: 95px; border: 0; resize: none; padding: 10px 19px; } button { bottom: 5px; left: 88px; } } } } .pmb-block { margin-bottom: 20px; @media (min-width: 1200px) { padding: 40px 42px 0; } @media (max-width: 1199px) { padding: 30px 20px 0; } &:last-child { margin-bottom: 50px; } .pmbb-header { margin-bottom: 25px; position: relative; .actions { position: absolute; top: -2px; right: 0; } h2 { margin: 0; font-weight: 100; font-size: 20px; } } .pmbb-edit { position: relative; z-index: 1; display: none; } .pmbb-edit, .pmbb-view { .animated(fadeIn, 1000ms); } &.toggled { .pmbb-edit { display: block; } .pmbb-view { display: none; } } } .pmo-block { padding: 25px; & > h2 { font-size: 16px; margin: 0 0 15px; } } .pmo-items { .pmob-body { padding: 0 10px; } a { display: block; padding: 4px; img { width: 100%; } } } } .pmo-contact { ul { list-style: none; margin: 0; padding: 0; li { position: relative; padding: 8px 0 8px 35px; i { font-size: 18px; vertical-align: top; line-height: 100%; position: absolute; left: 0; width: 18px; text-align: center; } } } } .pmo-map { margin: 20px -21px -18px; display: block; img { width: 100%; } } .c-timeline { @media(max-width: @screen-xs-max) { background: @body-bg; box-shadow: none; .tab-nav { background: #fff; box-shadow: 0 1px 1px rgba(0,0,0,.15); } } } .timeline { position: relative; @media(min-width: @screen-sm-min) { padding: 50px; padding-left: 100px; } @media(max-width: @screen-xs-max) { margin-top: 30px; } } .t-view { border: 1px solid #eee; position: relative; margin-bottom: 35px; @media(max-width: @screen-xs-max) { background: #fff; box-shadow: 0 1px 1px rgba(0,0,0,.15); } .tv-header { padding: 16px 18px; border-bottom: 1px solid #eee; background: #F9F9F9; .actions { position: absolute; top: 5px; right: 10px; } } .tv-body { padding: 23px 25px; .tvb-lightbox { margin: 0 -8px 15px; [data-src] { padding: 0 5px; margin-bottom: 5px; } } } .tvh-user { display: block; img { width: 46px; height: 46px; border-radius: 50%; } } &:before { position: absolute; width: 40px; height: 40px; border-radius: 50%; left: -70px; top: 0; border: 3px solid #FFF; text-align: center; font-size: 16px; line-height: 34px; color: #FFF; font-family: @font-icon-md; z-index: 1; } &:after { content: ""; position: absolute; top: 0; left: -50px; width: 1px; height: ~"calc(100% + 37px)"; } &[data-tv-type="text"] { &:before { content: "\f24f"; background: @m-cyan; box-shadow: 0 0 0 1px @m-cyan; } &:after { background: @m-cyan; } } &[data-tv-type="image"] { &:before { content: "\f17f"; background: @m-green; box-shadow: 0 0 0 1px @m-green; } &:after { background: @m-green; } } &[data-tv-type="video"] { &:before { content: "\f3a9"; background: @m-amber; box-shadow: 0 0 0 1px @m-amber; } &:after { background: @m-amber; } } .tvb-stats { list-style: none; padding: 0; margin: 10px 0 20px; & > li { display: inline-block; padding: 5px 10px 6px; border: 1px solid #ccc; margin-right: 2px; i { font-size: 15px; line-height: 100%; vertical-align: top; margin-top: 2px; } &.tvbs-comments { border-color: @m-green; color: @m-green; } &.tvbs-likes { border-color: @m-lightblue; color: @m-lightblue; } &.tvbs-views { border-color: @m-orange; color: @m-orange; } } } } .tv-comments { .tvc-lists { padding: 0; list-style: none; margin: 0; & > li { padding: 15px 20px; margin: 0; border-top: 1px solid #eee; } } } .tvc-more { color: #333; display: block; margin-bottom: -10px; &:hover { color: #000; } i { vertical-align: middle; margin-right: 5px; } } .p-header { position: relative; margin: 0 -7px; .actions { position: absolute; top: -18px; right: 0; } } .p-menu { list-style: none; padding: 0 5px; margin: 0 0 30px; & > li { display: inline-block; vertical-align: top; & > a { display: block; padding: 5px 20px 5px 0; font-weight: 500; text-transform: uppercase; font-size: 15px; & > i { margin-right: 4px; font-size: 20px; vertical-align: middle; margin-top: -5px; } } &:not(.active) > a { color: #4285F4; &:hover { color: #333; } } &.active > a { color: #000; } } .pm-search { @media(max-width: @screen-sm-max) { margin: 20px 2px 30px; display: block; input[type="text"] { width: 100%; border: 1px solid #ccc; } } } .pms-inner { margin: -2px 0 0; position: relative; top: -2px; overflow: hidden; white-space: nowrap; i { vertical-align: top; font-size: 20px; line-height: 100%; position: absolute; left: 9px; top: 8px; color: #333; } input[type="text"] { height: 35px; border-radius: 2px; padding: 0 10px 0 40px; @media(min-width: @screen-sm-min) { border: 1px solid #fff; width: 50px; background: transparent; position: relative; z-index: 1; .transition(all); .transition-duration(300ms); &:focus { border-color: #DFDFDF; width: 200px; } } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/progress-bar.less ================================================ .progress { box-shadow: none; border-radius: 0; height: 5px; margin-bottom: 0; .progress-bar { box-shadow: none; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/shadow.less ================================================ .z-depth { .z-depth-class(); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/sidebar.less ================================================ .sidebar { position: fixed; background: #fff; box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); height: ~"calc(100% - 65px)"; top: 65px; .transition(all); .transition-duration(300ms); z-index: 10; .opacity(0); overflow-y: auto; &.toggled { .opacity(1); } } #sidebar { width: @sidebar-left-width; .translate3d(-@sidebar-left-width, 0, 0); &.toggled { .translate3d(0, 0, 0); } } .profile-menu { & > a { display: block; height: 129px; margin-bottom: 5px; width: 100%; background: url(../img/profile-menu.png) no-repeat left top; background-size: 100%; .profile-pic { padding: 12px; & > img { width: 47px; height: 47px; border-radius: 50%; border: 3px solid rgba(0, 0, 0, 0.14); box-sizing: content-box; } } .profile-info { background: rgba(0, 0, 0, 0.37); padding: 7px 14px; color: #fff; margin-top: 20px; position: relative; & > i { font-size: 19px; line-height: 100%; position: absolute; right: 15px; top: 7px; .transition(all); .transition-duration(300ms); } } } .main-menu { display: none; margin: 0 0 0; border-bottom: 1px solid #E6E6E6; } &.toggled { .profile-info { & > i { .rotate(180deg) } } } } .main-menu { list-style: none; padding-left: 0; margin: 20px 0 0 0; & > li { & > a { padding: 14px 20px 14px 52px; display: block; color: #4C4C4C; font-weight: 500; position: relative; &:hover { color: #262626; background-color: #f7f7f7; } & > i { position: absolute; left: 16px; font-size: 20px; top: 0; width: 25px; text-align: center; padding: 13px 0; } } &.active { & > a { color: #262626; background-color: #F4F4F4; } } } } .sub-menu { & > a { position: relative; &:before, &:after { position: absolute; top: 12px; color: #575757; font-family: @font-icon-md; font-size: 17px; right: 15px; .transition(all); .transition-duration(250ms); .backface-visibility(hidden); } &:before { content: "\f278"; .scale(1); } &:after { content: "\f273"; .scale(0); } } .sub-menu > a { &:before, &:after { top: 5px; } } &.toggled { & > a { &:before { content: "\f278"; .scale(0); } &:after { content: "\f273"; .scale(1); } } } ul { list-style: none; display: none; padding: 0; & > li { & > a { color: #7f7f7f; padding: 8px 20px 8px 50px; font-weight: 500; display: block; &.active, &:hover { color: #000; } } &:first-child > a { padding-top: 14px; } &:last-child > a { padding-bottom: 16px; } ul { font-size: 12px; margin: 10px 0; background-color: @app-gray; } } } &.active { & > ul { display: block; } } } /* * layout */ body { &:not(.sw-toggled) { #sidebar { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } } &.sw-toggled { #sidebar { @media (min-width: @screen-lg-min) { .translate3d(0, 0, 0); .opacity(1); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } @media (max-width: @screen-md-max) { box-shadow: 0 0 10px rgba(51, 51, 51, 0.38); } } } } /* * For Stupid IE9 */ .ie9 { body.sw-toggled #sidebar { @media (min-width: @screen-lg-min) { display: block; } } body:not(.sw-toggled) { #sidebar:not(.toggled) { display: none; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/skin.less ================================================ /* * For header type 1 only * You may remove these if you opt header 2 */ #header { .skin-switch { padding: 10px 0 2px; text-align: center; } .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; display: inline-block; margin: 2px 3px; } } /* ----------------------------- End header type 1 ------------------------------------- */ /* * For header type 2 only * You may remove these if you opt header 1 */ #header-2 { .skin-switch { position: absolute; right: 50px; bottom: 23px; z-index: 1; .btn { background: #fff; width: 50px; height: 50px; border-radius: 50%; font-size: 25px; z-index: 2; } .dropdown-menu { min-width: 130px; height: 130px; border-radius: 50%; width: 130px; top: -42px; left: -40px; z-index: 1; .transform-origin(center); .scale-rotate(0, -360deg); .transition-duration(500ms); .ss-skin { position: absolute; &.ss-1 { margin-left: -8px; top: 12px; left: 50%; } &.ss-2 { right: 24px; top: 26px; } &.ss-3 { top: 50%; margin-top: -8px; right: 12px; } &.ss-4 { right: 24px; bottom: 26px; } &.ss-5 { margin-left: -8px; bottom: 12px; left: 50%; } &.ss-6 { left: 24px; bottom: 26px; } &.ss-7 { top: 50%; margin-top: -8px; left: 12px; } &.ss-8 { left: 24px; top: 26px; } } } &.open { .dropdown-menu { .scale-rotate(1, 0deg); } } } } /* ----------------------------- End header type 2 ------------------------------------- */ /* * Do not remove these * This is common for both */ .ss-skin { width: 16px; height: 16px; border-radius: 50%; cursor: pointer; &:hover { .opacity(0.8); } } .current-skin(@color) { background-color: @color; .ss-icon { color: @color; } .ha-menu { @media (max-width: @screen-xs-max) { background: @color; } } } [data-current-skin="lightblue"] { .current-skin(@m-lightblue); } [data-current-skin="bluegray"] { .current-skin(@m-bluegray); } [data-current-skin="blue"] { .current-skin(@m-blue); } [data-current-skin="purple"] { .current-skin(@m-purple); } [data-current-skin="orange"] { .current-skin(@m-orange); } [data-current-skin="cyan"] { .current-skin(@m-cyan); } [data-current-skin="green"] { .current-skin(@m-green); } [data-current-skin="teal"] { .current-skin(@m-teal); } [data-current-skin="pink"] { .current-skin(@m-pink); } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/table.less ================================================ .table { background-color: @table-bg; margin-bottom: 0; & > thead > tr > th { background-color: #fff; vertical-align: middle; font-weight: 500; color: #333; border-width: 1px; text-transform: uppercase; } [class*="bg-"] { & > tr > th { color: #fff; border-bottom: 0; } & + tbody > tr > td { border-top: 0; } } &.table-inner { border: 0; } & > thead > tr, & > tbody > tr, & > tfoot > tr { & > th, & > td { &:first-child { padding-left: 30px; } &:last-child { padding-right: 30px; } } } & > tbody, & > tfoot { & > tr { &.active, &.info, &.warning, &.succes, &.danger { & > td { border: 0; } } &:last-child > td > { padding-bottom: 20px; } } } } .table-striped { td, th { border: 0 !important; } } .table-bordered { border-bottom: 0; border-left: 0; border-right: 0; & > tbody > tr { & > td, & > th { border-bottom: 0; border-left: 0; &:last-child { border-right: 0; } } } & > thead > tr > th { border-left: 0; &:last-child { border-right: 0; } } } .table-vmiddle { td { vertical-align: middle !important; } } .table-responsive { border: 0; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/tabs.less ================================================ .tab-nav { list-style: none; padding: 0; white-space: nowrap; margin: 0; overflow: auto; box-shadow: inset 0 -2px 0 0 #eee; width: 100%; li { display: inline-block; vertical-align: top; & > a { display: inline-block; color: #7a7a7a; text-transform: uppercase; position: relative; width: 100%; .transition(all); .transition-duration(250ms); font-weight: 500; &:after { content: ""; height: 2px; position: absolute; width: 100%; left: 0; bottom: 0; .transition(all); .transition-duration(250ms); .scale(0); } @media (min-width: @screen-sm-min) { padding: 15px; } @media (max-width: @screen-sm-min) { padding: 15px 8px; } } &.active { & > a { color: #000; &:after { .scale(1); } } } } &.tab-nav-right { text-align: right; } &.tn-justified { & > li { display: table-cell; width: 1%; text-align: center; } } &.tn-icon { & > li { .zmdi { font-size: 22px; line-height: 100%; min-height: 25px; } } } &:not([data-tab-color]) { & > li > a:after { background: @m-blue; } } &[data-tab-color="green"] { & > li > a:after { background: @m-green; } } &[data-tab-color="red"] { & > li > a:after { background: @m-red; } } &[data-tab-color="teal"] { & > li > a:after { background: @m-teal; } } &[data-tab-color="amber"] { & > li > a:after { background: @m-amber; } } &[data-tab-color="black"] { & > li > a:after { background: @m-black; } } &[data-tab-color="cyan"] { & > li > a:after { background: @m-cyan; } } } .tab-content { padding: 20px 0; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/todo.less ================================================ #todo-lists { background: @m-amber; color: #fff; margin-bottom: 30px; font-family: 'shadowsintolight', cursive; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } .tl-header { position: relative; padding: 25px; & > h2 { margin: 0; color: #fff; line-height: 100%; } & > small { font-size: 17px; display: block; margin-top: 3px; } .actions { position: absolute; right: 10px; padding: 0; list-style: none; top: 15px; & > li { display: inline-block; vertical-align: baseline; } } } .tl-body { min-height: 300px; position: relative; padding: 20px 10px 20px 25px; background: rgba(0, 0, 0, 0.03); .media-body { padding-top: 3px; font-size: 18px; } .checkbox { margin-bottom: 15px; span { display: inline-block; margin-top: -3px; } input:checked + i + span { text-decoration: line-through; } .input-helper { &:before { border-color: rgba(255,255,255,0.8); border-width: 2px; } &:after { border-color: #fff; } } } } #add-tl-item { width: 50px; height: 50px; border-radius: 50%; position: absolute; background: #fff; top: -25px; right: 23px; .transition(all); .backface-visibility(hidden); .transition-duration(200ms); .add-new-item { .transition(all); .transition-duration(200ms); .scale(1); } .add-tl-body { overflow: hidden; .opacity(0); .transition(all); .transition-duration(300ms); textarea { padding: 25px 25px 45px; resize: none; width: 100%; font-size: 24px; color: @m-amber; position: absolute; height: 100%; border: 0; outline: none; } } &:not(.toggled) { overflow: hidden; .add-new-item { position: relative; z-index: 1; display: inline-block; width: 50px; height: 50px; .bg-option(); cursor: pointer; text-align: center; font-size: 23px; color: @m-orange; line-height: 50px; } } &.toggled { width: ~"calc(100% - 47px)"; height: ~"calc(100% - 25px)"; border-radius: 2px; top: 0; z-index: 1; box-shadow: 0 5px 8px rgba(0, 0, 0, 0.2); max-height: 300px; overflow: visible; .add-new-item { .scale(0); height: 0; overflow: hidden; float: left; } .add-tl-body { .opacity(1); .add-tl-actions { position: absolute; bottom: 0; width: 100%; padding: 5px 10px; border-top: 1px solid #EEE; z-index: 1; & > a { font-size: 25px; padding: 0 6px; border-radius: 50%; text-align: center; height: 40px; width: 40px; display: inline-block; line-height: 41px; border-radius: 50%; .transition(background-color); .transition-duration(300ms); &:hover { background-color: #eee; } } [data-tl-action="dismiss"] { color: @m-red; } [data-tl-action="save"] { color: @m-green; } } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/tooltip.less ================================================ .tooltip-inner { border-radius: 1px; padding: 3px 10px 5px; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/variables.less ================================================ /* * Font Icon Family */ @font-icon-md: 'Material-Design-Iconic-Font'; /* * Grid System */ @container-tablet: 100%; @container-desktop: 100%; /* Typography + Scaffolding + Links */ @body-bg: #edecec; @text-color: #5E5E5E; @font-family-sans-serif: roboto; @font-size-base: 13px; @link-hover-decoration: none; @headings-color: #000000; /* Border Radius */ @border-radius-base: 2px; @border-radius-large: 4px; @border-radius-small: 2px; /* Tabs */ @nav-tabs-border-color: #fff; @nav-tabs-active-link-hover-border-color: #fff; @nav-tabs-active-link-hover-bg: transparent; /* Form */ @input-border: #e0e0e0; @input-color: #000000; @input-border-radius: 0; @input-border-radius-large: 0px; @input-height-large: 40px; @input-height-base: 35px; @input-height-small: 30px; @input-border-focus: #b4b4b4; /* Table */ @table-bg: #fff; @table-border-color: #f0f0f0; @table-cell-padding: 10px; @table-condensed-cell-padding: 7px; @table-bg-accent: #f4f4f4; @table-bg-active: #FFFCBE; /* * Input Group */ @input-group-addon-bg: transparent; @input-group-addon-border-color: transparent; /* * Pagination */ @pagination-bg: #E2E2E2; @pagination-border: #fff; @pagination-color: #7E7E7E; @pagination-active-bg: @m-cyan; @pagination-active-border: @pagination-border; @pagination-disabled-bg: #E2E2E2; @pagination-disabled-border: @pagination-border; @pagination-hover-color: #333; @pagination-hover-bg: #d7d7d7; @pagination-hover-border: @pagination-border; @pager-border-radius: 5px; /* * Popover */ @popover-fallback-border-color: transparent; @popover-border-color: transparent; /* * Dropdown */ @dropdown-fallback-border: transparent; @dropdown-border: transparent; @dropdown-divider-bg: ''; @dropdown-link-hover-bg: rgba(0,0,0,0.075); @dropdown-link-color: #333; @dropdown-link-hover-color: #333; @dropdown-link-disabled-color: #e4e4e4; @dropdown-divider-bg: rgba(0,0,0,0.08); @dropdown-link-active-color: #333; @dropdown-link-active-bg: rgba(0, 0, 0, 0.075); @zindex-dropdown: 9; @dropdown-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); /* * Thumbnail */ @thumbnail-bg: #fff; /* * Alerts */ @alert-success-border: transparent; @alert-info-border: transparent; @alert-warning-border: transparent; @alert-danger-border: transparent; @alert-inverse-border: transparent; @alert-success-bg: fade(@m-green, 70%); @alert-info-bg: fade(@m-blue, 70%); @alert-warning-bg: fade(@m-amber, 70%); @alert-danger-bg: fade(@m-red, 70%); @alert-inverse-bg: #333; @alert-success-text: #fff; @alert-info-text: #fff; @alert-warning-text: #fff; @alert-danger-text: #fff; @alert-inverse-text: #fff; /* * Form Validations */ @state-success-text: lighten(@m-green, 8%); @state-warning-text: lighten(@m-orange, 8%); @state-danger-text: lighten(@m-red, 8%); @state-success-bg: lighten(@m-green, 8%); @state-warning-bg: lighten(@m-orange, 8%); @state-danger-bg: lighten(@m-red, 8%); /* * Buttons */ @border-radius-base: 2px; @border-radius-large: 2px; @border-radius-small: 2px; @btn-font-weight: 400; /* * Thumbnail */ @thumbnail-border: #EDEDED; @thumbnail-padding: 3px; /* * Carousel */ @carousel-caption-color: #fff; /* * Modal */ @modal-content-fallback-border-color: transparent; @modal-content-border-color: transparent; @modal-backdrop-bg: #000; @modal-header-border-color: transparent; @modal-title-line-height: transparent; @modal-footer-border-color: transparent; @zindex-modal-background: 11; /* * Tooltips */ @tooltip-bg: #737373; @tooltip-opacity: 1; /* * Popover */ @zindex-popover: 9; @popover-title-bg: #fff; @popover-border-color: #fff; @popover-fallback-border-color: #fff; /* * Breadcrumbs */ @breadcrumb-bg: transparent; @breadcrumb-padding-horizontal: 20px; @breadcrumb-active-color: #7c7c7c; /* * Jumbotron */ @jumbotron-bg: #F7F7F7; /* * List Groups */ @list-group-border: #E9E9E9; @list-group-active-color: #000; @list-group-active-bg: #f5f5f5; @list-group-active-border: #e9e9e9; @list-group-disabled-color: #B5B4B4; @list-group-disabled-bg: #fff; @list-group-disabled-text-color: #B5B4B4; /* * Badges */ @badge-color: #fff; @badge-bg: @brand-primary; @badge-border-radius: 2px; @badge-font-weight: 400; @badge-active-color: #fff; @badge-active-bg: @brand-primary; /* * Material Colors */ @m-white: #ffffff; @m-black: #000000; @m-blue: #2196F3; @m-red: #F44336; @m-purple: #9C27B0; @m-deeppurple: #673AB7; @m-lightblue: #03A9F4; @m-cyan: #00BCD4; @m-teal: #009688; @m-green: #4CAF50; @m-lightgreen: #8BC34A; @m-lime: #CDDC39; @m-yellow: #FFEB3B; @m-amber: #FFC107; @m-orange: #FF9800; @m-deeporange: #FF5722; @m-gray: #9E9E9E; @m-bluegray: #607D8B; @m-indigo: #3F51B5; @m-pink: #E91E63; @m-brown: #795548; /* Bootstrap Branding */ @brand-primary: @m-blue; @brand-success: @m-green; @brand-info: @m-cyan; @brand-warning: @m-orange; @brand-danger: @m-red; @app-gray: #F7F7F7; /* * Colors */ @ace: #F7F7F7; /* * Blocks */ @sidebar-left-width: 268px; @sidebar-right-width: 280px; @footer-height: 110px; @header-height: 70px; /* * Misc */ @card-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/bootgrid.less ================================================ .bootgrid-footer .infoBar, .bootgrid-header .actionBar { text-align: left; } .bootgrid-footer .search, .bootgrid-header .search { vertical-align: top; } .bootgrid-header { padding: 0 25px 10px; .search { border: 1px solid #e0e0e0; .form-control, .input-group-addon { border: 0; } .glyphicon-search { vertical-align: top; padding: 9px 10px 0; &:before { content: "\f1c3"; font-family: @font-icon-md; font-size: 17px; vertical-align: top; line-height: 100%; } } @media (min-width: @screen-xs-min) { width: 300px; } @media (max-width: @screen-xs-min) { width: 100%; padding-right: 90px; } } .actions { box-shadow: none; .btn-group { border: 1px solid #e0e0e0; .btn { height: 35px; box-shadow: none !important; background: transparent; } .dropdown-menu { padding: 10px 20px; .dropdown-item { padding: 0 0 0 27px !important; &:hover { background-color: #fff !important; } } @media (min-width: @screen-sm-min) { left: 0; .transform-origin(top left); margin-top: 1px; } } .caret { display: none; } .zmdi { line-height: 100%; font-size: 18px; vertical-align: top; .transition(all); .transition-duration(250ms); } &.open { .zmdi { .rotate(90deg); } } } @media (max-width: @screen-xs-min) { position: absolute; top: 0; right: 15px; } } } .bootgrid-table th > .column-header-anchor > .icon { top: 0px; font-size: 20px; line-height: 100%; } .bootgrid-footer { .col-sm-6 { padding: 10px 30px 20px; @media (max-width: @screen-sm-min) { text-align: center; } } .infoBar { @media (max-width: @screen-sm-min) { display: none; } .infos { border: 1px solid #EEE; display: inline-block; float: right; padding: 7px 30px; font-size: 12px; margin-top: 5px; } } } .select-cell { .checkbox { margin: 0; } } .command-edit, .command-delete { background: #fff; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/bootstrap-datetimepicker.less ================================================ .bootstrap-datetimepicker-widget { padding: 0 !important; margin: 0 !important; width: auto !important; &:after, &:before { display: none !important; } table td { text-shadow: none; span { margin: 0; &:hover { background: transparent; } } } .glyphicon { font-family: @font-icon-md; font-size: 18px; } .glyphicon-chevron-left:before { content: "\f2ff"; } .glyphicon-chevron-right:before { content: "\f301"; } .glyphicon-time:before { content: "\f337"; } .glyphicon-calendar:before { content: "\f32e"; } .glyphicon-chevron-up:before { content: "\f1e5"; } .glyphicon-chevron-down:before { content: "\f1e4"; } [data-action="togglePicker"] span { font-size: 25px; color: #ccc; &:hover { color: #333; } } a[data-action] { color: @m-teal; } } .timepicker-picker { .btn { box-shadow: none !important; } table { tbody tr + tr:not(:last-child) { background: @m-teal; color: #fff; td { border-radius: 0; } } } .btn, .btn:hover { background: #fff; color: #333; } } .datepicker { &.top { .transform-origin(0 100%) !important; } table { thead { tr { th { border-radius: 0; color: #fff; .glyphicon { width: 30px; height: 30px; border-radius: 50%; line-height: 29px; } &:hover .glyphicon { background: rgba(0, 0, 0, 0.2); } } &:first-child { th { background: @m-teal; padding: 20px 0; &:hover { background: @m-teal; } &.picker-switch { font-size: 16px; font-weight: 400; text-transform: uppercase; } } } &:last-child { th { &:first-child { padding-left: 20px; } &:last-child { padding-right: 20px; } text-transform: uppercase; font-weight: normal; font-size: 11px; } &:not(:only-child) { background: darken(@m-teal, 3%); } } } } tbody { tr { &:last-child { td { padding-bottom: 25px; } } td { &:first-child { padding-left: 13px; } &:last-child { padding-right: 13px; } } } } td { &.day { width: 35px; height: 35px; line-height: 20px; color: #333; position: relative; padding: 0; background: transparent; &:hover { background: none; } &:before { content: ""; width: 35px; height: 35px; border-radius: 50%; margin-bottom: -33px; display: inline-block; background: transparent; position: static; text-shadow: none; } &.old, &.new { color: #CDCDCD; } } &:not(.today):not(.active) { &:hover:before { background: #F0F0F0; } } &.today { color: #333; &:before { background-color: #E2E2E2; } } &.active { color: #fff; &:before { background-color: @m-teal; } } } } } .datepicker-months .month, .datepicker-years .year, .timepicker-minutes .minute, .timepicker-hours .hour { border-radius: 50%; &:not(.active) { &:hover { background: #F0F0F0; } } &.active { background: @m-teal; } } .timepicker-minutes .minute, .timepicker-hours .hour { padding: 0; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/bootstrap-select.less ================================================ .bootstrap-select { .dropdown-menu { padding: 0; } .dropdown-toggle:focus { outline: none !important; } & > .btn-default { background: none !important; border-bottom: 1px solid #e0e0e0 !important; border-radius: 0; padding-left: 0; padding-right: 0; &:before { position: absolute; top: 0; right: 0; content: ""; height: ~"calc(100% - 2px)"; width: 30px; background-color: #FFF; background-position: right ~"calc(100% - 7px)"; background-repeat: no-repeat; .img-retina('../img/select.png', '../img/select@2x.png', 12px, 12px); pointer-events: none; z-index: 5; } &:after { position: absolute; z-index: 3; bottom: -1px; left: 0; height: 2px; width: 0; content: ""; .transition(all); .transition-duration(300ms); } &:not(.disabled):after, &:not(.readonly):after { background: @m-blue; } &.disabled:after, &.readonly:after { background: #ccc; } } &.open { & > .btn-default:after { width: 100%; } } .bs-searchbox { padding: 5px 5px 5px 40px; position: relative; background: @ace; &:before { position: absolute; left: 0; top: 0; width: 40px; height: 100%; content: "\f1c3"; font-family: @font-icon-md; font-size: 25px; padding: 4px 0 0 15px; } input { border: 0; background: transparent; } } &.btn-group { .dropdown-menu li a.opt { padding-left: 17px; } } .check-mark { margin-top: -5px !important; font-size: 19px; .transition(all); .transition-duration(200ms); .scale(0); display: block !important; position: absolute; top: 11px; right: 15px; &:before { content: "\f26b"; font-family: @font-icon-md; } } .selected { .check-mark { .scale(1); } } .notify { bottom: 0 !important; margin: 0 !important; width: 100% !important; border: 0 !important; background: @m-red !important; color: #fff !important; text-align: center; } &:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) { width: 100%; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/chosen.less ================================================ .chosen-container { .chosen-drop { box-shadow: @dropdown-shadow; margin-top: 1px; border: 0; left: 0; .scale(0); .opacity(0); .transform-origin(top left); .transition(transform opacity); .transition-duration(250ms); } &.chosen-with-drop { .chosen-drop { .scale(1); .opacity(1); } .chosen-single:after { width: 100%; } } .chosen-results { margin: 0; padding: 0; max-height: 300px; li { padding: 10px 17px; width: 100%; &.highlighted { background: @dropdown-link-hover-bg; color: @dropdown-link-hover-color; } &.result-selected { background: transparent; color: @text-color; position: relative; &:before { content: "\f26b"; font-family: @font-icon-md; position: absolute; right: 15px; top: 10px; font-size: 19px; } } &.group-result { &:not(:first-child) { border-top: 1px solid #eee; } color: #B2B2B2; font-weight: normal; padding: 16px 15px 6px; margin-top: 9px; } } } } .chosen-container-single { .chosen-single { border-radius: 0; overflow: visible; height: 34px; padding: 6px 0 6px; text-transform: uppercase; border: 0; border-bottom: 1px solid @input-border; background: none; box-shadow: none; &:after { content: ""; width: 0; background: @m-blue; height: 2px; position: absolute; left: 0; bottom: -1px; .transition(width); .transition-duration(300ms); } div b { .img-retina('../img/select.png', '../img/select@2x.png', 12px, 12px); background-repeat: no-repeat; background-position: right 12px; } } .chosen-search { padding: 5px 5px 5px 40px; background: @ace; &:before { content: "\f1c3"; font-family: @font-icon-md; position: absolute; left: 0; top: 0; width: 40px; height: 100%; font-size: 25px; padding: 5px 0 0 15px; } input[type=text] { border: 0; height: 35px; line-height: 1.42857143; background: none; } } } .chosen-container-active.chosen-with-drop .chosen-single { border:0; background: none; } .chosen-container-multi { .chosen-choices { padding: 0; border: 0; border-bottom: 1px solid @input-border; background: none; box-shadow: none; li { &.search-choice { border-radius: 2px; margin: 4px 4px 0 0; background: darken(@ace, 5%); padding: 5px 23px 5px 8px; border: 0; box-shadow: none; .search-choice-close { background-image: none; &:before { display: inline-block; font-family: @font-icon-md; content: "\f135"; position: relative; top: 1px; color: #9C9C9C; z-index: 2; font-size: 12px; } } } &.search-field { input[type=text] { padding: 0; height: 31px; } } } } } select.chosen { display: none; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/farbtastic.less ================================================ .cp-container { position: relative; & > .input-group { input.cp-value { color: #000 !important; background: transparent !important; } .dropdown-menu { padding: 20px; margin-left: 10px; } } i.cp-value { width: 25px; height: 25px; border-radius: 2px; position: absolute; top: 0; right: 15px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/fileinput.less ================================================ .fileinput { position: relative; padding-right: 35px; .close { position: absolute; top: 5px; font-size: 12px; float: none; opacity: 1; font-weight: 500; border: 1px solid #ccc; width: 19px; text-align: center; height: 19px; line-height: 15px; border-radius: 50%; right: 0; &:hover { background: #eee; } } .btn-file { } .input-group-addon { padding: 0 10px; vertical-align: middle; } .fileinput-preview { width: 200px; height: 150px; position: relative; img { display: inline-block; vertical-align: middle; margin-top: -13px; } &:after { content: ""; display: inline-block; vertical-align: middle; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/fullcalendar.less ================================================ /** CALENDAR WIDGET **/ #calendar-widget { margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0,0,0,.15); } #fc-actions { position: absolute; bottom: 10px; right: 12px; } .fc { background-color: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, .15); margin-bottom: 30px; td, th { border-color: @table-border-color; } th { font-weight: 400; } table { background: transparent; tr { & > td:first-child { border-left-width: 0; } } } } #calendar-widget { .fc-toolbar { background: @m-teal; } .fc-day-header { color: #fff; background: darken(@m-teal, 5%); padding: 5px 0; border-width: 0; } .fc-day-number { text-align: center; color: #ADADAD; padding: 5px 0; } .fc-day-grid-event { margin: 1px 3px 1px; } .ui-widget-header th, .ui-widget-header { border-width: 0; } } #calendar { .fc-toolbar { height: 300px; .bg-cover('../img/cal-header.jpg'); background-position: inherit; &:before { content: ""; height: 50px; width: 100%; background: rgba(0, 0, 0, 0.36); position: absolute; bottom: 0; left: 0; } .fc-center { margin-top: 238px; position: relative; } @media screen and (max-width: @screen-sm-max) { height: 200px; .fc-center { margin-top: 138px; } } } .fc-day-header { color: #ADADAD; text-align: left; font-size: 14px; border-bottom-width: 0; border-right-color: #eee; padding: 10px 12px; } .fc-day-number { @media screen and (min-width: @screen-sm-max) { font-size: 25px; letter-spacing: -2px; } padding-left: 10px !important; color: #CCC; text-align: left !important; } .fc-day-grid-event { margin: 1px 9px 0; } } .fc-today { color: @m-amber; } .fc-toolbar { margin-bottom: 0; padding: 20px 7px 19px; position: relative; h2 { margin-top: 7px; font-size: 20px; font-weight: 400; text-transform: uppercase; color: #fff; } .ui-button { border: 0; background: 0 0; padding: 0; outline: none !important; text-align: center; width: 30px; height: 30px; border-radius: 50%; margin-top: 2px; color: #fff; &:hover { background: #fff; color: @m-teal; } & > span { position: relative; font-family: @font-icon-md; font-size: 20px; line-height: 100%; width: 30px; display: block; margin-top: 2px; &:before { position: relative; z-index: 1; } &.ui-icon-circle-triangle-w:before { content: "\f2fa"; } &.ui-icon-circle-triangle-e:before { content: "\f2fb"; } } } } .fc-event { padding: 0; font-size: 11px; border-radius: 0; border: 0; .fc-title { padding: 2px 8px; display: block; } .fc-time { float: left; background: rgba(0, 0, 0, 0.2); padding: 2px 6px; margin: 0 0 0 -1px; } } .fc-view, .fc-view > table { border: 0; overflow: hidden; } .fc-view > table > tbody > tr > .ui-widget-content { border-top: 0; } div.fc-row { margin-right: 0 !important; border: 0 !important; } .fc-today { color: @m-amber !important; } /* Even Tag Color */ .event-tag { margin-top: 5px; & > span { border-radius: 50%; width: 30px; height: 30px; margin-right: 3px; position: relative; display: inline-block; cursor: pointer; &:hover { .opacity(0.8); } &.selected { &:before { font-family: @font-icon-md; content: "\f26b"; position: absolute; text-align: center; top: 3px; width: 100%; font-size: 17px; color: #FFF; } } } } hr.fc-divider { border-width: 1px; border-color: #eee; } .fc-day-grid-container.fc-scroller { height: auto !important; overflow: hidden !important; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/light-gallery.less ================================================ #lg-slider { &:after { content: ""; -webkit-animation-fill-mode: both; animation-fill-mode: both; height: 50px; width: 50px; border-radius: 100%; border: 2px solid @m-blue; -webkit-animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8); position: absolute; left: 50%; margin-left: -25px; top: 50%; margin-top: -25px; z-index: -1; } } #lg-outer { background: rgba(255,255,255,0.95); .object { .z-depth(1); border-radius: 2px; } } #lg-close { display: none; } #lg-action { top: 0; width: 100%; left: 0; margin-left: 0 !important; height: 40px; text-align: center; & > a { background: transparent; color: #9D9D9D; font-size: 18px; width: 28px; height: 37px; &:hover { background: transparent; color: #000; } } .cl-thumb { position: fixed; right: 20px; bottom: 20px; width: 50px; height: 50px; border-radius: 50%; line-height: 38px; background: @m-red; .transition(all); .transition-duration(300ms); .z-depth(1); &:after { text-align: center; left: 16px !important; bottom: 6px !important; color: #fff; } &:hover { background: darken(@m-red, 5%); } } } #lg-gallery .thumb-cont { background: @m-red; text-align: center; .thumb-info { background: @m-red; .count { display: none; } .close { width: 14px; margin-top: 0; background: none; &:hover { background: none; } } } .thumb { .opacity(1); } .thumb-inner { display: inline-block; padding: 12px 12px 15px; } } .lg-slide { background: none !important; em { font-style: normal; h3 { margin-bottom: 5px; } } .video-cont { .z-depth(2); } } @-webkit-keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } @keyframes ball-scale-ripple { 0% { -webkit-transform: scale(0.1); transform: scale(0.1); opacity: 1; } 70% { -webkit-transform: scale(1); transform: scale(1); opacity: 0.7; } 100% { opacity: 0.0; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/malihu-custom-scrollbar.less ================================================ .mCSB_scrollTools { width: 5px; .mCSB_dragger_bar { border-radius: 0 !important; } &.mCSB_scrollTools_horizontal, &.mCSB_scrollTools_vertical { margin: 0 !important; } &.mCSB_scrollTools_horizontal { height: 10px; } } .mCS-minimal-dark { &.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar { background: rgba(0,0,0,0.4); } &.mCSB_scrollTools_onDrag .mCSB_dragger .mCSB_dragger_bar { background: rgba(0,0,0,0.5) !important; } } .mCSB_inside > .mCSB_container { margin-right: 0; } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/mediaelement.less ================================================ .mejs-container { outline: none; .mejs-controls { background: #ec592f; height: 50px; padding: 10px 5px 0; div { height: 5px; } div.mejs-time-rail { position: absolute; left: 0; top: 0; padding: 0; width: 100% !important; .mejs-time-total { margin: 0; width: 100% !important; background: #ec592f; } .mejs-time-loaded { background: #D04B25; } .mejs-time-current { background: #ffea00; } .mejs-time-buffering { background: #ec592f; } span:not(.mejs-time-float), a { border-radius: 0; height: 3px; } } .mejs-button { button { background-color: #ec592f; width: 15px; height: 15px; background-position: center; &:focus { outline: none !important; } } } .mejs-volume-button { position: absolute; right: 35px; } .mejs-play button { .img-retina('../img/icons/play.png', '../img/icons/play@2x.png', 15px, 15px); } .mejs-pause button { .img-retina('../img/icons/pause.png', '../img/icons/pause@2x.png', 15px, 15px); } .mejs-mute button { .img-retina('../img/icons/speaker.png', '../img/icons/speaker@2x.png', 15px, 15px); } .mejs-unmute button { .img-retina('../img/icons/speaker-2.png', '../img/icons/speaker-2@2x.png', 15px, 15px); } .mejs-fullscreen-button { position: absolute; right: 5px; button { .img-retina('../img/icons/fullscreen.png', '../img/icons/fullscreen@2x.png', 15px, 15px); } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/noUiSlider.less ================================================ .noUi-target { border-radius: 0; box-shadow: none; border: 0; } .noUi-background { background: #d4d4d4; box-shadow: none; } .noUi-horizontal { height: 3px; .noUi-handle { top: -8px; } } .noUi-vertical { width: 3px; } .noUi-horizontal, .noUi-vertical { .noUi-handle { width: 19px; height: 19px; border: 0; border-radius: 100%; box-shadow: none; .transition(box-shadow); .transition-duration(200ms); cursor: pointer; position: relative; &:before, &:after { display: none; } &:active { background: #ccc !important; } .is-tooltip { position: absolute; bottom: 32px; height: 35px; border-radius: 2px; color: #fff; text-align: center; line-height: 33px; width: 50px; left: 50%; margin-left: -25px; padding: 0 10px; .transition(all); .transition-duration(200ms); .backface-visibility(hidden); .opacity(0); .scale(0); &:after { width: 0; height: 0; border-style: solid; border-width: 15px 10px 0 10px; position: absolute; bottom: -8px; left: 50%; margin-left: -9px; content: ""; } } } .noUi-active { box-shadow: 0 0 0 13px rgba(0,0,0,0.1); .is-tooltip { .scale(1); bottom: 40px; .opacity(1); } } } .input-slider, .input-slider-range, .input-slider-values { &:not([data-is-color]) { .noUi-handle, .noUi-connect, { background: @m-teal !important; } .is-tooltip { background: @m-teal; &:after { border-color: @m-teal transparent transparent transparent; } } } &[data-is-color=red] { .is-color-handle(@m-red); } &[data-is-color=blue] { .is-color-handle(@m-blue); } &[data-is-color=cyan] { .is-color-handle(@m-cyan); } &[data-is-color=amber] { .is-color-handle(@m-amber); } &[data-is-color=green] { .is-color-handle(@m-green); } } .input-slider { .noUi-origin { background: #d4d4d4; } &:not([data-is-color]) { .noUi-base { background: @m-teal !important; } } &[data-is-color=red] { .is-color-base(@m-red); } &[data-is-color=blue] { .is-color-base(@m-blue); } &[data-is-color=cyan] { .is-color-base(@m-cyan); } &[data-is-color=amber] { .is-color-base(@m-amber); } &[data-is-color=green] { .is-color-base(@m-green); } } .is-color-handle(@color) { .noUi-handle, .noUi-connect { background: @color !important; } } .is-color-base(@color) { .noUi-base { background: @color !important; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/summernote.less ================================================ .note-editor, .note-popover { .note-toolbar, .popover-content { background: #fff; border-color: #e4e4e4; margin: 0; padding: 10px 0 15px; text-align: center; & > .btn-group { display: inline-block; float: none; box-shadow: none; .btn { margin: 0 1px; } & > .active { background: @m-cyan; color: #fff; } } .btn { height: 40px; border-radius: 2px !important; box-shadow: none !important; &:active { box-shadow: none; } } .note-palette-title { margin: 0 !important; padding: 10px 0 !important; font-size: 13px !important; text-align: center !important; border: 0 !important; } .note-color-reset { padding: 0 0 10px !important; margin: 0 !important; background: none; text-align: center; } .note-color { .dropdown-menu { min-width: 335px; } } } .note-statusbar { .note-resizebar { border-color: #E8E8E8; .note-icon-bar { border-color: #BCBCBC; } } } .fa { font-style: normal; font-size: 20px; vertical-align: middle; &:before { font-family: @font-icon-md; } &.fa-magic:before { content: "\f16a"; } &.fa-bold:before { content: "\f23d"; } &.fa-italic:before { content: "\f245"; } &.fa-underline:before { content: "\f24f"; } &.fa-font:before { content: "\f242"; } &.fa-list-ul:before { content: "\f247"; } &.fa-list-ol:before { content: "\f248"; } &.fa-align-left:before { content: "\f23b"; } &.fa-align-right:before { content: "\f23c"; } &.fa-align-center:before { content: "\f239"; } &.fa-align-justify:before { content: "\f23a"; } &.fa-indent:before { content: "\f244"; } &.fa-outdent:before { content: "\f243"; } &.fa-text-height:before { content: "\f246"; } &.fa-table:before { content: "\f320"; } &.fa-link:before { content: "\f18e"; } &.fa-picture-o:before { content: "\f17f"; } &.fa-minus:before { content: "\f22f"; } &.fa-arrows-alt:before { content: "\f16d"; } &.fa-code:before { content: "\f13a"; } &.fa-question:before { content: "\f1f5"; } &.fa-eraser:before { content: "\f23f"; } &.fa-square:before { content: "\f279"; } &.fa-circle-o:before { content: "\f26c"; } &.fa-times:before { content: "\f136"; } } .note-air-popover { .arrow { left: 20px; } } } .note-editor { overflow: visible; border: 1px solid #e4e4e4; .note-editable { padding: 20px 23px; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/sweetalert.less ================================================ .sweet-alert { border-radius: 2px; padding: 10px 30px; h2 { font-size: 16px; font-weight: 400; position: relative; z-index: 1; } .lead { font-size: 13px; } .btn { padding: 6px 12px; font-size: 13px; margin: 20px 2px 0; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/typeahead.less ================================================ .twitter-typeahead { width: 100%; .tt-menu { min-width: 200px; background: #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); display: block !important; z-index: 2 !important; .scale(0); .opacity(0); .transition(all); .transition-duration(300ms); .backface-visibility(hidden); .transform-origin(top left); &.tt-open:not(.tt-empty) { .scale(1); .opacity(1); } } .tt-suggestion { padding: 8px 17px; color: #333; cursor: pointer; } .tt-suggestion:hover, .tt-cursor { background-color: rgba(0,0,0,0.075); } .tt-hint { color: #818181 !important; } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/vendor-overrides/waves.less ================================================ ================================================ FILE: material-manage/src/main/webapp/static/less/inc/wall.less ================================================ .wall-attrs { margin-bottom: 0; } .wa-stats { float: left; & > span { margin-right: -1px; padding: 7px 12px; border: 1px solid #E0E0E0; float: left; font-weight: 500; &.active { color: @m-green; } &:first-child { border-radius: 2px 0 0 2px; } &:last-child { border-radius: 0 2px 2px 0; } & > i { line-height: 100%; vertical-align: top; position: relative; top: 2px; font-size: 15px; margin-right: 2px; } } } .wa-users { float: right; padding: 0 !important; margin-right: -5px; & > a { display: inline-block; margin-left: 2px; & > img { width: 33px; height: 33px; border-radius: 50%; &:hover { .opacity(0.85); } } } } .wcc-inner { border: 1px solid #E4E4E4; padding: 10px 15px; resize: none; border-radius: 2px; background: #fff; color: #9A9A9A; cursor: pointer; } .wcci-text { border: 0; display: block; width: 100%; resize: none; padding: 0; } .wall-comment-list { padding: 20px; background: @app-gray; .media { position: relative; &:hover { .actions { display: block; } } } .actions { display: none; position: absolute; right: -20px; top: -1px; } } .wcl-list + .wcl-form { margin-top: 25px; } .wp-text { border: 0; padding: 0; display: block; width: 100%; resize: none; } .wp-media { background: @app-gray; border: 1px solid #E4E4E4; padding: 12px 15px; margin-top: 25px; text-align: center; } .wpb-actions { background: @app-gray; margin: 0; padding: 10px 20px; & > li:not(.pull-right) { float: left; } } .wall-attr-types(@color) { color: @color; &:hover { color: darken(@color, 5%); } } [data-wpba="image"] { .wall-attr-types(@m-green); } [data-wpba="video"] { .wall-attr-types(@m-orange); } [data-wpba="link"] { .wall-attr-types(@m-cyan); } .wpba-attrs { & > ul { & > li { padding: 0; margin-right: 5px; & > a { display: block; width: 22px; & > i { font-size: 20px; } } &.active i { color: #333; } } } } .wall-img-preview { text-align: center; @media screen and (min-width: @screen-sm-min) { margin: 0 -23px 20px; } @media screen and (max-width: @screen-sm-max) { margin: 0 -16px 20px; } .wip-item { display: block; float: left; position: relative; overflow: hidden; border: 2px solid #fff; .bg-cover-inline(); & > img { display: none; } &:first-child:nth-last-child(2), &:first-child:nth-last-child(2) ~ div { width: 50%; padding-bottom: 40%; } &:first-child:nth-last-child(3), &:first-child:nth-last-child(3) ~ div, &:first-child:nth-last-child(4), &:first-child:nth-last-child(4) ~ div:not(:last-child), &:first-child:nth-last-child(5), &:first-child:nth-last-child(5) ~ div:not(:nth-last-of-type(-n+2)), &:first-child:nth-last-child(6), &:first-child:nth-last-child(6) ~ div, &:first-child:nth-last-child(7) ~ div:nth-last-of-type(-n+3) { width: 33.333333%; padding-bottom: 30%; } &:first-child:nth-last-child(5) ~ div:nth-last-of-type(-n+2) { width: 50%; padding-bottom: 40%; } &:first-child:nth-last-child(7), &:first-child:nth-last-child(7) ~ div:not(:nth-last-of-type(-n+3)), &:first-child:nth-last-child(n+8), &:first-child:nth-last-child(n+8) ~ div { width: 25%; padding-bottom: 22%; } &:only-child, &:first-child:nth-last-child(4) ~ div:nth-child(4) { width: 100%; padding-bottom: 50%; } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/widgets.less ================================================ .dash-widget-item { position: relative; min-height: 380px; margin-bottom: 30px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); .dash-widget-header { position: relative; .actions { display: none; position: absolute; right: 4px; top: 6px; } } .dash-widget-footer { position: absolute; left: 0; bottom: 0; width: 100%; } .dash-widget-title { padding: 12px 20px; position: absolute; width: 100%; left: 0; font-weight: 300; } &:hover { .dash-widget-header .actions { display: block; } } } /* * Site Visits */ #site-visits { color: rgba(255, 255, 255, 0.9); .dash-widget-header { padding-bottom: 38px; background-color: rgba(0,0,0,0.13); } .dash-widget-title { bottom: 0; background: rgba(0,0,0,0.15); color: rgba(255,255,255,0.9); } h3 { color: rgba(255, 255, 255, 0.9); } } /* * Best Selling Item */ #best-selling { background-color: #fff; .dash-widget-header { & > img { width: 100%; height: 155px; } .dash-widget-title { padding-bottom: 30px; top: 0; color: #fff; #gradient > .vertical(rgba(0,0,0,0.6); rgba(0,0,0,0)); } .main-item { padding: 15px; color: #fff; position: absolute; bottom: 0; left: 0; width: 100%; #gradient > .vertical(rgba(0,0,0,0); rgba(0,0,0,0.6)); & > h2 { font-weight: 400; font-size: 20px; margin: 5px 0 0 0; line-height: 100%; color: #fff; } } } } /* * Weather */ #weather-widget { color: #fff; padding: 20px 20px 0; .weather-status { font-size: 40px; line-height: 100%; } .weather-icon { text-align: center; margin-top: 10px; height: 150px; .bg-option(); /* Weather Icons */ .wi-item(@icon) { .img-retina('../img/icons/weather/@{icon}.png', '../img/icons/weather/@{icon}@2x.png', 125px, 125px); } &.wi-0 { .wi-item(0); } &.wi-1 { .wi-item(1); } &.wi-2 { .wi-item(2); } &.wi-3 { .wi-item(3); } &.wi-4 { .wi-item(2); } &.wi-5 { .wi-item(5); } &.wi-6 { .wi-item(5); } &.wi-7 { .wi-item(5); } &.wi-8 { .wi-item(5); } &.wi-9 { .wi-item(9); } &.wi-10 { .wi-item(5); } &.wi-11 { .wi-item(9); } &.wi-12 { .wi-item(9); } &.wi-13 { .wi-item(9); } &.wi-14 { .wi-item(9); } &.wi-15 { .wi-item(5); } &.wi-16 { .wi-item(9); } &.wi-17 { .wi-item(5); } &.wi-18 { .wi-item(18); } &.wi-19 { .wi-item(19); } &.wi-20 { .wi-item(19); } &.wi-21 { .wi-item(19); } &.wi-22 { .wi-item(19); } &.wi-23 { .wi-item(19); } &.wi-24 { .wi-item(24); } &.wi-25 { .wi-item(24); } &.wi-26 { .wi-item(26); } &.wi-27 { .wi-item(27); } &.wi-28 { .wi-item(28); } &.wi-29 { .wi-item(27); } &.wi-30 { .wi-item(28); } &.wi-31 { .wi-item(31); } &.wi-32 { .wi-item(32); } &.wi-33 { .wi-item(31); } &.wi-34 { .wi-item(32); } &.wi-35 { .wi-item(5); } &.wi-36 { .wi-item(32); } &.wi-37 { .wi-item(2); } &.wi-38 { .wi-item(2); } &.wi-39 { .wi-item(2); } &.wi-40 { .wi-item(5); } &.wi-41 { .wi-item(5); } &.wi-42 { .wi-item(9); } &.wi-43 { .wi-item(5); } &.wi-44 { .wi-item(27); } &.wi-45 { .wi-item(2); } &.wi-46 { .wi-item(18); } &.wi-47 { .wi-item(2); } } .weather-info { list-style: none; padding: 0; margin: 3px 0 0 0; & > li { display: inline-block; border: 1px solid rgba(255, 255, 255, 0.39); padding: 2px 10px 3px; margin-right: 5px; } } .weather-list { background: rgba(0, 0, 0, 0.08); padding: 5px 12px; font-size: 16px; height: 51px; .text-overflow(); & > span { margin-right: 7px; font-weight: 300; display: inline-block; line-height: 40px; vertical-align: top; .wli-icon(@icon) { background-image: url('../img/icons/weather/@{icon}.png'); } &.weather-list-icon { width: 35px; height: 35px; .bg-option(); background-size: 30px 30px; &.wi-0 { .wli-icon(0); } &.wi-1 { .wli-icon(1); } &.wi-2 { .wli-icon(2); } &.wi-3 { .wli-icon(3); } &.wi-4 { .wli-icon(2); } &.wi-5 { .wli-icon(5); } &.wi-6 { .wli-icon(5); } &.wi-7 { .wli-icon(5); } &.wi-8 { .wli-icon(5); } &.wi-9 { .wli-icon(9); } &.wi-10 { .wli-icon(5); } &.wi-11 { .wli-icon(9); } &.wi-12 { .wli-icon(9); } &.wi-13 { .wli-icon(9); } &.wi-14 { .wli-icon(9); } &.wi-15 { .wli-icon(5); } &.wi-16 { .wli-icon(9); } &.wi-17 { .wli-icon(5); } &.wi-18 { .wli-icon(18); } &.wi-19 { .wli-icon(19); } &.wi-20 { .wli-icon(19); } &.wi-21 { .wli-icon(19); } &.wi-22 { .wli-icon(19); } &.wi-23 { .wli-icon(19); } &.wi-24 { .wli-icon(24); } &.wi-25 { .wli-icon(24); } &.wi-26 { .wli-icon(26); } &.wi-27 { .wli-icon(27); } &.wi-28 { .wli-icon(28); } &.wi-29 { .wli-icon(27); } &.wi-30 { .wli-icon(28); } &.wi-31 { .wli-icon(31); } &.wi-32 { .wli-icon(32); } &.wi-33 { .wli-icon(31); } &.wi-34 { .wli-icon(32); } &.wi-35 { .wli-icon(5); } &.wi-36 { .wli-icon(32); } &.wi-37 { .wli-icon(2); } &.wi-38 { .wli-icon(2); } &.wi-39 { .wli-icon(2); } &.wi-40 { .wli-icon(5); } &.wi-41 { .wli-icon(5); } &.wi-42 { .wli-icon(9); } &.wi-43 { .wli-icon(5); } &.wi-44 { .wli-icon(27); } &.wi-45 { .wli-icon(2); } &.wi-46 { .wli-icon(18); } &.wi-47 { .wli-icon(2); } } & > i { line-height: 100%; font-size: 39px; } } } } /* * Pie Charts */ #pie-charts { background: #fff; .dash-widget-header { color: rgba(255, 255, 255, 0.9); } } /* * Blog Post */ .blog-post { .bp-header { position: relative; & > img { width: 100%; } .bp-title { background: @m-indigo; width: 100%; padding: 20px; color: #FFF; display: block; & > h2 { color: #FFF; font-weight: 400; margin: 0 0 2px; line-height: 100%; font-size: 21px; } } } } /* * Profile View */ .profile-view { text-align: center; .pv-header { position: relative; height: 145px; width: 100%; .bg-cover('../img/headers/sm/4.png'); & > .pv-main { border-radius: 50%; width: 130px; position: absolute; height: 130px; bottom: -50px; left: 50%; margin-left: -65px; .transition(all); .transition-duration(300ms); } } .pv-body { margin-top: 70px; padding: 0 20px 20px; & > h2 { margin: 0; line-height: 100%; font-size: 20px; font-weight: 400; } & > small { display: block; color: #8E8E8E; margin: 10px 0 15px; } .pv-contact, .pv-follow { padding: 0; list-style: none; & > li { display: inline-block; } } .pv-follow { margin: 20px -20px; padding: 10px; background-color: #F7F7F7; border-top: 1px solid #EEE; border-bottom: 1px solid #EEE; & > li { padding: 0 10px; } } .pv-contact { & > li { margin: 0 5px; & > .zmdi { line-height: 100%; vertical-align: text-bottom; font-size: 22px; } } } .pv-follow-btn { padding: 7px 20px; background: @m-cyan; color: #FFF; border-radius: 3px; text-transform: uppercase; display: block; .transition(all); .transition-duration(300ms); &:hover { background: darken(@m-cyan, 5%); } } } &:hover { .pv-main { .scale(1.2); } } } /* * Picture List */ .picture-list { .pl-body { padding: 2px; [class*="col-"] { padding: 0; padding: 2px; & > a { display: block; .hover-pop(rgba(0,0,0,0.3)); img { width: 100%; } } } .clearfix(); } } /* * Social */ .go-social { .card-body { padding: 0 15px 20px; [class*="col-"] { padding: 12px; img { .transition(all); .transition-duration(200ms); .backface-visibility(hidden); } &:hover { img { .scale(1.2); } } } } } /* * Rating */ .rating-list { padding: 0 0 10px; .rl-star { margin-top: 10px; margin-bottom: 4px; .zmdi { font-size: 20px; &:not(.active) { color: #ccc; } &.active { color: @m-orange; } } } .lv-item { .media { .zmdi-star { line-height: 100%; font-size: 22px; color: #FF9800; vertical-align: middle; position: relative; top: -2px; left: 6px; } .media-body { padding: 7px 10px 0 5px; } } } } ================================================ FILE: material-manage/src/main/webapp/static/less/inc/wizard.less ================================================ .fw-container { .tab-content { padding: 25px 0; } .fw-footer { text-align: center; margin: 30px 0 0; width: 100%; border-top: 2px solid #eee; padding: 15px 0; } } ================================================ FILE: material-manage/src/main/webapp/static/modular/message/history/message.js ================================================ /** * 系统参数管理初始化 */ var Message = { id: "MessageTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Message.initColumn = function () { return [ {field: 'selectItem', radio: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '消息模板', field: 'tplCode', visible: true, align: 'center', valign: 'middle'}, {title: '消息内容', field: 'content', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ console.log(row); return ''+data+'' }}, {title: '接收者', field: 'receiver', visible: true, align: 'center', valign: 'middle'}, {title: '发送时间', field: 'createTime', visible: true, align: 'center', valign: 'middle'} ]; }; /** * 打开查看系统参数详情 */ Message.openDetail = function (id) { var index = layer.open({ type: 2, title: '消息详情', area: ['800px', '300px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/message/history/view/' + id }); this.layerIndex = index; }; /** * 删除系统参数 */ Message.clear = function () { var operation = function() { var ajax = new $ax(Feng.ctxPath + "/message/history/clear", function (data) { Feng.success("清除成功!"); Message.table.refresh(); }, function (data) { Feng.error("清除失败!" + data.responseJSON.message + "!"); }); ajax.start(); }; Feng.confirm("清除后将无法恢复,确认该操作?", operation); }; /** * 查询系统参数列表 */ Message.search = function () { var queryData = {}; queryData['condition'] = $("#condition").val(); Message.table.refresh({query: queryData}); }; $(function () { var defaultColunms = Message.initColumn(); var table = new BSTable(Message.id, "/message/history/list", defaultColunms); table.setPaginationType("server"); Message.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/message/sender/sender.js ================================================ /** * 消息发送器管理初始化 */ var MessageSender = { id: "MessageSenderTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ MessageSender.initColumn = function () { return [ {field: 'selectItem', radio: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '名称', field: 'name', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, {title: '发送类', field: 'className', visible: true, align: 'center', valign: 'middle'}, {title: '运营商短信模板编号', field: 'tplCode', visible: true, align: 'center', valign: 'middle'}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加发送器 */ MessageSender.openAdd = function () { var index = layer.open({ type: 2, title: '添加发送器', area: ['60%', '45%'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/message/sender/add' }); this.layerIndex = index; }; /** * 打开查看发送器详情 */ MessageSender.openDetail = function (id) { var index = layer.open({ type: 2, title: '发送器详情', area: ['60%', '50%'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/message/sender/update/' + id }); this.layerIndex = index; }; /** * 删除发送器 */ MessageSender.delete = function (id) { var operation = function(){ var ajax = new $ax(Feng.ctxPath + "/message/sender", function (data) { Feng.success("删除成功!"); MessageSender.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("id",id); ajax.setType("delete"); ajax.start(); }; Feng.confirm("是否删除消息发送器?",operation); }; $(function () { var defaultColunms = MessageSender.initColumn(); var table = new BSTable(MessageSender.id, "/message/sender/list", defaultColunms); table.setPaginationType("server"); MessageSender.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/message/sender/sender_info.js ================================================ /** * 初始化发送器详情对话框 */ var MessageSenderInfoDlg = { formData : {} }; /** * 清除数据 */ MessageSenderInfoDlg.clearData = function() { this.formData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MessageSenderInfoDlg.set = function(key, val) { this.formData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MessageSenderInfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ MessageSenderInfoDlg.close = function() { parent.layer.close(window.parent.MessageSender.layerIndex); } /** * 收集数据 */ MessageSenderInfoDlg.collectData = function() { this .set('id') .set('name') .set('className') .set('tplCode'); } /** * 提交添加 */ MessageSenderInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/message/sender", function(data){ Feng.success("添加成功!"); window.parent.MessageSender.table.refresh(); MessageSenderInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.formData); ajax.start(); } /** * 提交修改 */ MessageSenderInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/message/sender", function(data){ Feng.success("修改成功!"); window.parent.MessageSender.table.refresh(); MessageSenderInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.formData); ajax.start(); } $(function() { }); ================================================ FILE: material-manage/src/main/webapp/static/modular/message/template/template.js ================================================ /** * 消息模板管理初始化 */ var MessageTemplate = { id: "MessageTemplateTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ MessageTemplate.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '编号', field: 'code', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, {title: '标题', field: 'title', visible: true, align: 'center', valign: 'middle'}, {title: '内容', field: 'content', visible: true, align: 'center', valign: 'middle'}, {title: '发送条件', field: 'cond', visible: true, align: 'center', valign: 'middle'}, {title: '类型', field: 'typeName', visible: true, align: 'center', valign: 'middle'}, {title: '模板', field: 'messageSender.name', visible: true, align: 'center', valign: 'middle'}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加模板 */ MessageTemplate.openAdd = function () { var index = layer.open({ type: 2, title: '添加模板', area: ['60%', '380px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/message/template/add' }); this.layerIndex = index; }; /** * 打开查看模板详情 */ MessageTemplate.openDetail = function (id) { var index = layer.open({ type: 2, title: '模板详情', area: ['60%', '380px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/message/template/update/' + id }); this.layerIndex = index; }; /** * 删除模板 */ MessageTemplate.delete = function (id) { var operation = function() { var ajax = new $ax(Feng.ctxPath + "/message/template", function (data) { Feng.success("删除成功!"); MessageTemplate.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("id", id); ajax.setType("delete"); ajax.start(); }; Feng.confirm("是否刪除模板?", operation); }; $(function () { var defaultColunms = MessageTemplate.initColumn(); var table = new BSTable(MessageTemplate.id, "/message/template/list", defaultColunms); table.setPaginationType("server"); MessageTemplate.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/message/template/template_info.js ================================================ /** * 初始化发送器详情对话框 */ var MessageTemplateInfoDlg = { formData : {} }; /** * 清除数据 */ MessageTemplateInfoDlg.clearData = function() { this.formData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MessageTemplateInfoDlg.set = function(key, val) { this.formData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MessageTemplateInfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ MessageTemplateInfoDlg.close = function() { parent.layer.close(window.parent.MessageTemplate.layerIndex); } /** * 收集数据 */ MessageTemplateInfoDlg.collectData = function() { this .set('id') .set('code') .set('title') .set('content') .set('cond') .set('type') .set('idMessageSender'); } /** * 提交添加 */ MessageTemplateInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/message/template", function(data){ Feng.success("添加成功!"); window.parent.MessageTemplate.table.refresh(); MessageTemplateInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.formData); ajax.start(); } /** * 提交修改 */ MessageTemplateInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/message/template", function(data){ Feng.success("修改成功!"); window.parent.MessageTemplate.table.refresh(); MessageTemplateInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.formData); ajax.start(); } $(function() { }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/cfg/cfg.js ================================================ /** * 系统参数管理初始化 */ var Cfg = { id: "CfgTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Cfg.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '参数名', field: 'cfgName', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, {title: '参数值', field: 'cfgValue', visible: true, align: 'center', valign: 'middle'}, {title: '参数描述', field: 'cfgDesc', visible: true, align: 'center', valign: 'middle'}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加系统参数 */ Cfg.openAddCfg = function () { var index = layer.open({ type: 2, title: '添加系统参数', area: ['65%', '280px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/cfg/cfg_add' }); this.layerIndex = index; }; /** * 打开系统参数详情页 */ Cfg.openCfgDetail = function (id) { var index = layer.open({ type: 2, title: '系统参数详情', area: ['65%', '280px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/cfg/cfg_update/' + id }); this.layerIndex = index; }; /** * 删除系统参数 */ Cfg.delete = function (id) { var operation = function() { var ajax = new $ax(Feng.ctxPath + "/cfg/delete", function (data) { Feng.success("删除成功!"); Cfg.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.setType('delete'); ajax.set("cfgId", id); ajax.start(); }; Feng.confirm("确认删除该记录?", operation); }; /** * 查询系统参数列表 */ Cfg.search = function () { var queryData = {}; queryData['cfgName'] = $("#cfgName").val(); queryData['cfgValue'] = $("#cfgValue").val(); Cfg.table.refresh({query: queryData}); }; Cfg.reset = function () { $("#cfgName").val(""); $("#cfgValue").val(""); this.search(); }; $(function () { var defaultColunms = Cfg.initColumn(); var table = new BSTable(Cfg.id, "/cfg/list", defaultColunms); table.setPaginationType("server"); Cfg.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/cfg/cfg_info.js ================================================ /** * 初始化系统参数详情对话框 */ var CfgInfoDlg = { cfgInfoData : {} }; /** * 清除数据 */ CfgInfoDlg.clearData = function() { this.cfgInfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ CfgInfoDlg.set = function(key, val) { this.cfgInfoData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ CfgInfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ CfgInfoDlg.close = function() { parent.layer.close(window.parent.Cfg.layerIndex); } /** * 收集数据 */ CfgInfoDlg.collectData = function() { this .set('id') .set('cfgName') .set('cfgValue') .set('cfgDesc'); } /** * 提交添加 */ CfgInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/cfg/add", function(data){ Feng.success("添加成功!"); window.parent.Cfg.table.refresh(); CfgInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.cfgInfoData); ajax.start(); } /** * 提交修改 */ CfgInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/cfg/update", function(data){ Feng.success("修改成功!"); window.parent.Cfg.table.refresh(); CfgInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.cfgInfoData); ajax.start(); } $(function() { }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/dept/dept.js ================================================ /** * 部门管理初始化 */ var Dept = { id: "DeptTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Dept.initColumn = function () { return [ {title: 'id', field: 'id', align: 'center', valign: 'middle',width:'50px'}, {title: '部门简称', field: 'simplename', align: 'center', valign: 'middle', sortable: true,formatter:function(data,row){ return ''+row.simplename+''; }}, {title: '部门全称', field: 'fullname', align: 'center', valign: 'middle', sortable: true}, {title: '排序', field: 'num', align: 'center', valign: 'middle', sortable: true}, {title: '备注', field: 'tips', align: 'center', valign: 'middle', sortable: true}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加部门 */ Dept.openAddDept = function () { var index = layer.open({ type: 2, title: '添加部门', area: ['800px', '320px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/dept/dept_add' }); this.layerIndex = index; }; /** * 打开查看部门详情 */ Dept.openDeptDetail = function (id) { var index = layer.open({ type: 2, title: '部门详情', area: ['800px', '320px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/dept/dept_update/' + id }); this.layerIndex = index; }; /** * 删除部门 */ Dept.delete = function (id) { var operation = function(){ var ajax = new $ax(Feng.ctxPath + "/dept/delete", function () { Feng.success("删除成功!"); Dept.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("deptId",id); ajax.start(); }; Feng.confirm("是否刪除该部门?", operation); }; /** * 查询部门列表 */ Dept.search = function () { var queryData = {}; queryData['condition'] = $("#condition").val(); Dept.table.refresh({query: queryData}); }; $(function () { var defaultColunms = Dept.initColumn(); var table = new BSTreeTable(Dept.id, "/dept/list", defaultColunms); table.setExpandColumn(2); table.setIdField("id"); table.setCodeField("id"); table.setParentCodeField("pid"); table.setExpandAll(true); table.init(); Dept.table = table; }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/dept/dept_info.js ================================================ /** * 初始化部门详情对话框 */ var DeptInfoDlg = { deptInfoData : {}, zTreeInstance : null, validateFields: { simplename: { validators: { notEmpty: { message: '部门名称不能为空' } } }, fullname: { validators: { notEmpty: { message: '部门全称不能为空' } } }, pName: { validators: { notEmpty: { message: '上级名称不能为空' } } } } }; /** * 清除数据 */ DeptInfoDlg.clearData = function() { this.deptInfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ DeptInfoDlg.set = function(key, val) { this.deptInfoData[key] = (typeof value == "undefined") ? $("#" + key).val() : value; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ DeptInfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ DeptInfoDlg.close = function() { parent.layer.close(window.parent.Dept.layerIndex); } /** * 点击部门ztree列表的选项时 * * @param e * @param treeId * @param treeNode * @returns */ DeptInfoDlg.onClickDept = function(e, treeId, treeNode) { $("#pName").attr("value", DeptInfoDlg.zTreeInstance.getSelectedVal()); $("#pid").attr("value", treeNode.id); } /** * 显示部门选择的树 * * @returns */ DeptInfoDlg.showDeptSelectTree = function() { var pName = $("#pName"); var pNameOffset = $("#pName").offset(); $("#parentDeptMenu").css({ left : pNameOffset.left + "px", top : pNameOffset.top + pName.outerHeight() + "px" }).slideDown("fast"); $("body").bind("mousedown", onBodyDown); } /** * 隐藏部门选择的树 */ DeptInfoDlg.hideDeptSelectTree = function() { $("#parentDeptMenu").fadeOut("fast"); $("body").unbind("mousedown", onBodyDown);// mousedown当鼠标按下就可以触发,不用弹起 } /** * 收集数据 */ DeptInfoDlg.collectData = function() { this.set('id').set('simplename').set('fullname').set('tips').set('num').set('pid'); } /** * 验证数据是否为空 */ DeptInfoDlg.validate = function () { $('#deptInfoForm').data("bootstrapValidator").resetForm(); $('#deptInfoForm').bootstrapValidator('validate'); return $("#deptInfoForm").data('bootstrapValidator').isValid(); } /** * 提交添加部门 */ DeptInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/dept/add", function(data){ Feng.success("添加成功!"); window.parent.Dept.table.refresh(); DeptInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.deptInfoData); ajax.start(); } /** * 提交修改 */ DeptInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/dept/update", function(data){ Feng.success("修改成功!"); window.parent.Dept.table.refresh(); DeptInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.deptInfoData); ajax.start(); } function onBodyDown(event) { if (!(event.target.id == "menuBtn" || event.target.id == "parentDeptMenu" || $( event.target).parents("#parentDeptMenu").length > 0)) { DeptInfoDlg.hideDeptSelectTree(); } } $(function() { Feng.initValidator("deptInfoForm", DeptInfoDlg.validateFields); var ztree = new $ZTree("parentDeptMenuTree", "/dept/tree"); ztree.bindOnClick(DeptInfoDlg.onClickDept); ztree.init(); DeptInfoDlg.zTreeInstance = ztree; }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/dict/dict.js ================================================ /** * 字典管理初始化 */ var Dict = { id: "DictTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Dict.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'id', field: 'id', visible: false, align: 'center', valign: 'middle'}, {title: '名称', field: 'name', align: 'center', valign: 'middle', sortable: true,formatter:function(data,row){ return ''+data+''; }}, {title: '详情', field: 'detail', align: 'center', valign: 'middle', sortable: true}, {title: '备注', field: 'tips', align: 'center', valign: 'middle', sortable: true}, {title: '操作',formatter:function(data,row){ return ''; }} ]; }; /** * 点击添加字典 */ Dict.openAddDict = function () { var index = layer.open({ type: 2, title: '添加字典', area: ['800px', '420px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/dict/dict_add' }); this.layerIndex = index; }; /** * 打开查看字典详情 */ Dict.openDictDetail = function (id) { var index = layer.open({ type: 2, title: '字典详情', area: ['800px', '420px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/dict/dict_edit/' + id }); this.layerIndex = index; }; /** * 删除字典 */ Dict.delete = function (id) { var operation = function(){ var ajax = new $ax(Feng.ctxPath + "/dict/delete", function (data) { Feng.success("删除成功!"); Dict.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("dictId", id); ajax.start(); }; Feng.confirm("确认删除该记录?", operation); }; /** * 查询字典列表 */ Dict.search = function () { var queryData = {}; queryData['condition'] = $("#condition").val(); Dict.table.refresh({query: queryData}); }; $(function () { var defaultColunms = Dict.initColumn(); var table = new BSTable(Dict.id, "/dict/list", defaultColunms); table.setPaginationType("client"); Dict.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/dict/dict_info.js ================================================ /** * 初始化字典详情对话框 */ var DictInfoDlg = { count: $("#itemSize").val(), dictName: '', //字典的名称 mutiString: '', //拼接字符串内容(拼接字典条目) itemTemplate: $("#itemTemplate").html() }; /** * item获取新的id */ DictInfoDlg.newId = function () { if(this.count == undefined){ this.count = 0; } this.count = this.count + 1; return "dictItem" + this.count; }; /** * 关闭此对话框 */ DictInfoDlg.close = function () { parent.layer.close(window.parent.Dict.layerIndex); }; /** * 添加条目 */ DictInfoDlg.addItem = function () { $("#itemsArea").append(this.itemTemplate); $("#dictItem").attr("id", this.newId()); }; /** * 删除item */ DictInfoDlg.deleteItem = function (event) { var obj = Feng.eventParseObject(event); obj = obj.is('button') ? obj : obj.parent(); obj.parent().parent().remove(); }; /** * 清除为空的item Dom */ DictInfoDlg.clearNullDom = function(){ $("[name='dictItem']").each(function(){ var num = $(this).find("[name='itemNum']").val(); var name = $(this).find("[name='itemName']").val(); if(num == '' || name == ''){ $(this).remove(); } }); }; /** * 收集添加字典的数据 */ DictInfoDlg.collectData = function () { this.clearNullDom(); var mutiString = ""; $("[name='dictItem']").each(function(){ var num = $(this).find("[name='itemNum']").val(); var name = $(this).find("[name='itemName']").val(); mutiString = mutiString + (num + ":" + name + ";"); }); this.dictName = $("#dictName").val(); this.mutiString = mutiString; }; /** * 提交添加字典 */ DictInfoDlg.addSubmit = function () { this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/dict/add", function (data) { Feng.success("添加成功!"); window.parent.Dict.table.refresh(); DictInfoDlg.close(); }, function (data) { Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set('dictName',this.dictName); ajax.set('dictValues',this.mutiString); ajax.start(); }; /** * 提交修改 */ DictInfoDlg.editSubmit = function () { this.collectData(); var ajax = new $ax(Feng.ctxPath + "/dict/update", function (data) { Feng.success("修改成功!"); window.parent.Dict.table.refresh(); DictInfoDlg.close(); }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set('dictId',$("#dictId").val()); ajax.set('dictName',this.dictName); ajax.set('dictValues',this.mutiString); ajax.start(); }; ================================================ FILE: material-manage/src/main/webapp/static/modular/system/menu/menu.js ================================================ /** * 角色管理的单例 */ var Menu = { id: "menuTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Menu.initColumn = function () { var columns = [ {title: 'ID', field: 'id', visible: false, align: 'center', valign: 'middle'}, {title: '菜单名称', field: 'name', align: 'center', valign: 'middle', sortable: true, width: '17%',formatter:function(data,row){ return ''+row.name+''; }}, {title: '菜单编号', field: 'code', align: 'center', valign: 'middle', sortable: true, width: '12%'}, {title: '菜单父编号', field: 'pcode', align: 'center', valign: 'middle', sortable: true}, {title: '请求地址', field: 'url', align: 'center', valign: 'middle', sortable: true, width: '15%'}, {title: '排序', field: 'num', align: 'center', valign: 'middle', sortable: true}, {title: '层级', field: 'levels', align: 'center', valign: 'middle', sortable: true}, {title: '是否是菜单', field: 'isMenuName', align: 'center', valign: 'middle', sortable: true}, {title: '状态', field: 'statusName', align: 'center', valign: 'middle', sortable: true}, {title: '操作',formatter:function(data,row){ return ''; }} ] return columns; }; /** * 点击添加菜单 */ Menu.openAddMenu = function () { var index = layer.open({ type: 2, title: '添加菜单', area: ['850px', '380px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/menu/menu_add' }); this.layerIndex = index; }; /** * 点击修改 */ Menu.openChangeMenu = function (id) { var index = layer.open({ type: 2, title: '修改菜单', area: ['850px', '380px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/menu/menu_edit/' + id }); this.layerIndex = index; }; /** * 删除 */ Menu.delMenu = function (id) { var operation = function () { var ajax = new $ax(Feng.ctxPath + "/menu/remove", function (data) { Feng.success("删除成功!"); Menu.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("menuId", id); ajax.start(); }; Feng.confirm("是否删除该记录?", operation); }; /** * 搜索 */ Menu.search = function () { var queryData = {}; queryData['menuName'] = $("#menuName").val(); queryData['level'] = $("#level").val(); Menu.table.refresh({query: queryData}); } $(function () { var defaultColunms = Menu.initColumn(); var table = new BSTreeTable(Menu.id, "/menu/list", defaultColunms); table.setExpandColumn(2); table.setIdField("id"); table.setCodeField("code"); table.setParentCodeField("pcode"); table.setExpandAll(false); table.init(); Menu.table = table; }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/menu/menu_info.js ================================================ /** * 菜单详情对话框 */ var MenuInfoDlg = { menuInfoData: {}, ztreeInstance: null, validateFields: { name: { validators: { notEmpty: { message: '菜单名称不能为空' } } }, code: { validators: { notEmpty: { message: '菜单编号不能为空' } } }, pcodeName: { validators: { notEmpty: { message: '父菜单不能为空' } } }, url: { validators: { notEmpty: { message: '请求地址不能为空' } } }, num: { validators: { notEmpty: { message: '序号不能为空' } } } } }; /** * 清除数据 */ MenuInfoDlg.clearData = function () { this.menuInfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MenuInfoDlg.set = function (key, val) { this.menuInfoData[key] = (typeof value == "undefined") ? $("#" + key).val() : value; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ MenuInfoDlg.get = function (key) { return $("#" + key).val(); } /** * 关闭此对话框 */ MenuInfoDlg.close = function () { parent.layer.close(window.parent.Menu.layerIndex); } /** * 收集数据 */ MenuInfoDlg.collectData = function () { this.set('id').set('name').set('code').set('pcode').set('url').set('num').set('levels').set('icon').set("ismenu"); } /** * 验证数据是否为空 */ MenuInfoDlg.validate = function () { $('#menuInfoForm').data("bootstrapValidator").resetForm(); $('#menuInfoForm').bootstrapValidator('validate'); return $("#menuInfoForm").data('bootstrapValidator').isValid(); } /** * 提交添加用户 */ MenuInfoDlg.addSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/menu/add", function (data) { Feng.success("添加成功!"); window.parent.Menu.table.refresh(); MenuInfoDlg.close(); }, function (data) { Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.menuInfoData); ajax.start(); } /** * 提交修改 */ MenuInfoDlg.editSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/menu/edit", function (data) { Feng.success("修改成功!"); window.parent.Menu.table.refresh(); MenuInfoDlg.close(); }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.menuInfoData); ajax.start(); } /** * 点击父级编号input框时 */ MenuInfoDlg.onClickDept = function (e, treeId, treeNode) { $("#pcodeName").attr("value", MenuInfoDlg.ztreeInstance.getSelectedVal()); $("#pcode").attr("value", treeNode.code); }; /** * 显示父级菜单选择的树 */ MenuInfoDlg.showMenuSelectTree = function () { Feng.showInputTree("pcodeName", "pcodeTreeDiv", 15, 34); }; $(function () { Feng.initValidator("menuInfoForm", MenuInfoDlg.validateFields); var ztree = new $ZTree("pcodeTree", "/menu/selectMenuTreeList"); ztree.bindOnClick(MenuInfoDlg.onClickDept); ztree.init(); MenuInfoDlg.ztreeInstance = ztree; //初始化是否是菜单 if($("#ismenuValue").val() == undefined){ $("#ismenu").val(0); }else{ $("#ismenu").val($("#ismenuValue").val()); } }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/role/role.js ================================================ /** * 角色管理的单例 */ var Role = { id: "roleTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Role.initColumn = function () { var columns = [ {field: 'selectItem', checkbox: true }, {title: 'id', field: 'id', visible: false, align: 'center', valign: 'middle'}, {title: '名称', field: 'name', align: 'center', valign: 'middle', sortable: true,formatter:function(data,row){ return ''+data+''; }}, {title: '上级角色', field: 'pName', align: 'center', valign: 'middle', sortable: true}, {title: '所在部门', field: 'deptName', align: 'center', valign: 'middle', sortable: true}, {title: '别名', field: 'tips', align: 'center', valign: 'middle', sortable: true}, {title: '操作',formatter:function(data,row){ return '' + ''; }} ] return columns; }; /** * 点击添加管理员 */ Role.openAddRole = function () { var index = layer.open({ type: 2, title: '添加角色', area: ['800px', '330px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/role/role_add' }); this.layerIndex = index; }; /** * 点击修改按钮时 */ Role.openChangeRole = function (id) { var index = layer.open({ type: 2, title: '修改角色', area: ['800px', '330px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/role/role_edit/' + id }); this.layerIndex = index; }; /** * 删除角色 */ Role.delRole = function (id) { var operation = function(){ var ajax = new $ax(Feng.ctxPath + "/role/remove", function () { Feng.success("删除成功!"); Role.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("roleId", id); ajax.start(); }; Feng.confirm("确认删除该角色?",operation); }; /** * 权限配置 */ Role.assign = function (id) { var index = layer.open({ type: 2, title: '权限配置', area: ['300px', '550px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/role/role_assign/' +id }); this.layerIndex = index; }; /** * 搜索角色 */ Role.search = function () { var queryData = {}; queryData['roleName'] = $("#roleName").val(); Role.table.refresh({query: queryData}); } $(function () { var defaultColunms = Role.initColumn(); var table = new BSTable(Role.id, "/role/list", defaultColunms); table.setPaginationType("client"); table.init(); Role.table = table; }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/role/role_info.js ================================================ /** * 角色详情对话框(可用于添加和修改对话框) */ var RolInfoDlg = { roleInfoData: {}, deptZtree: null, pNameZtree: null, validateFields: { name: { validators: { notEmpty: { message: '用户名不能为空' } } }, tips: { validators: { notEmpty: { message: '别名不能为空' } } }, pName: { validators: { notEmpty: { message: '父级名称不能为空' } } } } }; /** * 清除数据 */ RolInfoDlg.clearData = function () { this.roleInfoData = {}; }; /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ RolInfoDlg.set = function (key, val) { this.roleInfoData[key] = (typeof value == "undefined") ? $("#" + key).val() : value; return this; }; /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ RolInfoDlg.get = function (key) { return $("#" + key).val(); }; /** * 关闭此对话框 */ RolInfoDlg.close = function () { parent.layer.close(window.parent.Role.layerIndex); }; /** * 点击部门input框时 * * @param e * @param treeId * @param treeNode * @returns */ RolInfoDlg.onClickDept = function (e, treeId, treeNode) { $("#deptName").attr("value", RolInfoDlg.deptZtree.getSelectedVal()); $("#deptid").attr("value", treeNode.id); }; RolInfoDlg.onDblClickDept = function (e, treeId, treeNode) { $("#deptName").attr("value", RolInfoDlg.deptZtree.getSelectedVal()); $("#deptid").attr("value", treeNode.id); $("#deptContent").fadeOut("fast"); }; /** * 点击父级菜单input框时 * * @param e * @param treeId * @param treeNode * @returns */ RolInfoDlg.onClickPName = function (e, treeId, treeNode) { $("#pName").attr("value", RolInfoDlg.pNameZtree.getSelectedVal()); $("#pid").attr("value", treeNode.id); }; /** * 显示部门选择的树 * * @returns */ RolInfoDlg.showDeptSelectTree = function () { Feng.showInputTree("deptName", "deptContent"); }; /** * 显示父级菜单的树 * * @returns */ RolInfoDlg.showPNameSelectTree = function () { Feng.showInputTree("pName", "pNameContent"); }; /** * 收集数据 */ RolInfoDlg.collectData = function () { this.set('id').set('name').set('pid').set('deptid').set('tips').set('num'); }; /** * 验证数据是否为空 */ RolInfoDlg.validate = function () { $('#roleInfoForm').data("bootstrapValidator").resetForm(); $('#roleInfoForm').bootstrapValidator('validate'); return $("#roleInfoForm").data('bootstrapValidator').isValid(); }; /** * 提交添加用户 */ RolInfoDlg.addSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/role/add", function (data) { Feng.success("添加成功!"); window.parent.Role.table.refresh(); RolInfoDlg.close(); }, function (data) { Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.roleInfoData); ajax.start(); }; /** * 提交修改 */ RolInfoDlg.editSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/role/edit", function (data) { Feng.success("修改成功!"); window.parent.Role.table.refresh(); RolInfoDlg.close(); }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.roleInfoData); ajax.start(); }; $(function () { Feng.initValidator("roleInfoForm", RolInfoDlg.validateFields); var deptTree = new $ZTree("deptTree", "/dept/tree"); deptTree.bindOnClick(RolInfoDlg.onClickDept); deptTree.bindOnDblClick(RolInfoDlg.onDblClickDept) deptTree.init(); RolInfoDlg.deptZtree = deptTree; var pNameTree = new $ZTree("pNameTree", "/role/roleTreeList"); pNameTree.bindOnClick(RolInfoDlg.onClickPName); pNameTree.init(); RolInfoDlg.pNameZtree = pNameTree; }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/task/task.js ================================================ /** * 系统参数管理初始化 */ var Task = { id: "TaskTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ Task.initColumn = function () { return [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: true, align: 'center', valign: 'middle'}, {title: '任务名', field: 'name', visible: true, align: 'center', valign: 'middle',formatter:function(data,row){ return ''+data+''; }}, {title: '执行类', field: 'jobClass', visible: true, align: 'center', valign: 'middle'}, {title: '定时规则', field: 'cron', visible: true, align: 'center', valign: 'middle'}, {title: '任务说明', field: 'note', visible: true, align: 'center', valign: 'middle'}, {title: '最近执行时间',field:'execAt',visible:true,align:'center',valign:'middle'}, {title: '最近执行结果',field:'execResult',visible:true,align:'center',valign:'middle'}, {title: '状态',field:'disabled',visible:true,align:'center',valign:'middle',formatter:function(data,row){ var button = ''; console.log(data); if(!data) { button += '
                                  '; }else{ button += '
                                  '; } return button; }}, {title: '操作', visible: true, align: 'center', valign: 'middle', formatter:function(data,row){ var button = ''; button += ''; button += ''; return button }} ]; }; Task.enable = function(id){ console.log(id); Task.updateDisalbed(id,false) } Task.disable = function(id){ console.log(id); Task.updateDisalbed(id,true); } Task.updateDisalbed=function(id,disabled){ var url = Feng.ctxPath; console.log(disabled); if(disabled){ //禁用 url += '/task/disable'; }else{ //启用 url += '/task/enable'; } var ajax = new $ax(url, function (data) { Feng.success(disabled?'禁用成功':'启用成功'); Task.table.refresh(); }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set("taskId",id); ajax.start(); } /** * 检查是否选中 */ Task.check = function () { var selected = $('#' + this.id).bootstrapTable('getSelections'); if(selected.length == 0){ Feng.info("请先选中表格中的某一记录!"); return false; }else{ Task.seItem = selected[0]; return true; } }; /** * 点击添加系统参数 */ Task.openAddTask = function () { var index = layer.open({ type: 2, title: '添加系统参数', area: ['65%', '370px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/task/task_add' }); this.layerIndex = index; }; /** * 打开查看系统参数详情 */ Task.openTaskDetail = function (id) { var index = layer.open({ type: 2, title: '系统参数详情', area: ['65%', '400px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/task/task_update/' + id }); this.layerIndex = index; }; Task.viewLog = function (id) { var index = layer.open({ type: 2, title: '查看任务日志', area: ['75%', '75%'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/task/viewLog/' + id }); this.layerIndex = index; }; /** * 删除系统参数 */ Task.delete = function (id) { var operation = function() { var ajax = new $ax(Feng.ctxPath + "/task/delete", function (data) { Feng.success("删除成功!"); Task.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("taskId", id); ajax.start(); }; Feng.confirm("确认删除该记录?", operation); }; /** * 查询系统参数列表 */ Task.search = function () { var queryData = {}; queryData['condition'] = $("#name").val(); Task.table.refresh({query: queryData}); }; $(function () { var defaultColunms = Task.initColumn(); var table = new BSTable(Task.id, "/task/list", defaultColunms); table.setPaginationType("client"); Task.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/task/task_info.js ================================================ /** * 初始化系统参数详情对话框 */ var TaskInfoDlg = { taskInfoData : {} }; /** * 清除数据 */ TaskInfoDlg.clearData = function() { this.taskInfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ TaskInfoDlg.set = function(key, val) { this.taskInfoData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ TaskInfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ TaskInfoDlg.close = function() { parent.layer.close(window.parent.Task.layerIndex); } /** * 收集数据 */ TaskInfoDlg.collectData = function() { this .set('id') .set('name') .set('jobClass') .set('data') .set('note') .set('cron'); } /** * 提交添加 */ TaskInfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/task/add", function(data){ Feng.success("添加成功!"); window.parent.Task.table.refresh(); TaskInfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.taskInfoData); ajax.start(); } /** * 提交修改 */ TaskInfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/task/update", function(data){ Feng.success("修改成功!"); window.parent.Task.table.refresh(); TaskInfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.taskInfoData); ajax.start(); } $(function() { }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/task/task_log.js ================================================ /** * 系统参数管理初始化 */ var TaskLog = { id: "TaskLogTable", //表格id seItem: null, //选中的条目 table: null, layerIndex: -1 }; /** * 初始化表格的列 */ TaskLog.initColumn = function () { return [ {field: 'selectItem', radio: true,visible:false}, {title: '执行任务', field: 'name', visible: true, align: 'center', valign: 'middle'}, {title: '执行日期', field: 'execAt', visible: true, align: 'center', valign: 'middle'}, { title: '执行结果',field: 'execSuccess',visible: true,align: 'center',valign: 'middle', formatter: function (data, row) { if (data == 1) { return "执行成功"; } return "执行失败"; } }, {title: '异常信息', field: 'jobException', visible: true, align: 'center', valign: 'middle'} ]; }; $(function () { var defaultColunms = TaskLog.initColumn(); var table = new BSTable(TaskLog.id, "/task/logList/"+taskId, defaultColunms); table.setPaginationType("server"); TaskLog.table = table.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/user/user.js ================================================ /** * 系统管理--用户管理的单例对象 */ var MgrUser = { id: "managerTable",//表格id seItem: null, //选中的条目 table: null, layerIndex: -1, deptid:0 }; /** * 初始化表格的列 */ MgrUser.initColumn = function () { var columns = [ {field: 'selectItem', checkbox: true}, {title: 'ID', field: 'id', visible: false, align: 'center', valign: 'middle'}, {title: '账号', field: 'account', align: 'center', valign: 'middle', sortable: true,formatter:function(data,row){ return ''+row.account+''; }}, {title: '姓名', field: 'name', align: 'center', valign: 'middle', sortable: true}, {title: '性别', field: 'sexName', align: 'center', valign: 'middle', sortable: true}, {title: '角色', field: 'roleName', align: 'center', valign: 'middle', sortable: true,formatter:function(data,row){ if(data && data.length>5){ return ''; } return data; }}, {title: '部门', field: 'deptName', align: 'center', valign: 'middle', sortable: true}, {title: '邮箱', field: 'email', align: 'center', valign: 'middle', sortable: true}, {title: '电话', field: 'phone', align: 'center', valign: 'middle', sortable: true}, {title: '创建时间', field: 'createTime', align: 'center', valign: 'middle', sortable: true}, {title: '状态',field:'status',visible:true,align:'center',valign:'middle',formatter:function(data,row){ if(data == 3){ return '已删除'; } if(data == 1) { return '
                                  '; } if(data == 2){ return '
                                  '; } }}, {title: '操作',formatter:function(data,row){ return '' + '' + ''; }} ]; return columns; }; /** * 检查是否选中 */ MgrUser.check = function () { var selected = $('#' + this.id).bootstrapTable('getSelections'); if (selected.length == 0) { Feng.info("请先选中表格中的某一记录!"); return false; } else { MgrUser.seItem = selected[0]; return true; } }; /** * 点击添加管理员 */ MgrUser.openAddMgr = function () { var index = layer.open({ type: 2, title: '添加管理员', area: ['70%', '500px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/mgr/user_add' }); this.layerIndex = index; }; /** * 点击修改按钮时 * @param userId 管理员id */ MgrUser.openChangeUser = function (id) { var index = layer.open({ type: 2, title: '编辑管理员', area: ['800px', '380px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/mgr/user_edit/' + id }); this.layerIndex = index; }; /** * 点击角色分配 * @param */ MgrUser.roleAssign = function (id) { var index = layer.open({ type: 2, title: '角色分配', area: ['300px', '400px'], //宽高 fix: false, //不固定 maxmin: true, content: Feng.ctxPath + '/mgr/role_assign/' + id }); this.layerIndex = index; }; /** * 删除用户 */ MgrUser.delMgrUser = function (id) { var operation = function(){ var ajax = new $ax(Feng.ctxPath + "/mgr/delete", function () { Feng.success("删除成功!"); MgrUser.table.refresh(); }, function (data) { Feng.error("删除失败!" + data.responseJSON.message + "!"); }); ajax.set("userId", id); ajax.start(); }; Feng.confirm("是否删除该账户?",operation,"删除后该用户将无法恢复,请谨慎操作"); }; /** * 冻结用户账户 * @param userId */ MgrUser.freezeAccount = function (id) { console.log(id) var ajax = new $ax(Feng.ctxPath + "/mgr/freeze", function (data) { Feng.success("冻结成功!"); MgrUser.table.refresh(); }, function (data) { Feng.error("冻结失败!" + data.responseJSON.message + "!"); }); ajax.set("userId", id); ajax.start(); }; /** * 解除冻结用户账户 * @param userId */ MgrUser.unfreeze = function (id) { console.log(id) var ajax = new $ax(Feng.ctxPath + "/mgr/unfreeze", function (data) { Feng.success("解除冻结成功!"); MgrUser.table.refresh(); }, function (data) { Feng.error("解除冻结失败!"); }); ajax.set("userId", id); ajax.start(); } /** * 重置密码 */ MgrUser.resetPwd = function (id) { Feng.confirm('是否重置密码为111111?', function () { var ajax = new $ax(Feng.ctxPath + "/mgr/reset", function (data) { Feng.success("重置密码成功!"); }, function (data) { Feng.error("重置密码失败!"); }); ajax.set("userId", id); ajax.start(); }); }; MgrUser.resetSearch = function () { $("#name").val(""); $("#beginTime").val(""); $("#endTime").val(""); MgrUser.search(); } MgrUser.search = function () { var queryData = {}; queryData['deptid'] = MgrUser.deptid; queryData['name'] = $("#name").val(); queryData['beginTime'] = $("#beginTime").val(); queryData['endTime'] = $("#endTime").val(); MgrUser.table.refresh({query: queryData}); } MgrUser.onClickDept = function (e, treeId, treeNode) { MgrUser.deptid = treeNode.id; MgrUser.search(); }; $(function () { var defaultColunms = MgrUser.initColumn(); var table = new BSTable("managerTable", "/mgr/list", defaultColunms); table.setPaginationType("client"); MgrUser.table = table.init(); var ztree = new $ZTree("deptTree", "/dept/tree"); ztree.bindOnClick(MgrUser.onClickDept); ztree.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/modular/system/user/user_info.js ================================================ /** * 用户详情对话框(可用于添加和修改对话框) */ var UserInfoDlg = { userInfoData: {}, validateFields: { account: { validators: { notEmpty: { message: '账户不能为空' } } }, name: { validators: { notEmpty: { message: '姓名不能为空' } } }, citySel: { validators: { notEmpty: { message: '部门不能为空' } } }, password: { validators: { notEmpty: { message: '密码不能为空' }, identical: { field: 'rePassword', message: '两次密码不一致' }, } }, rePassword: { validators: { notEmpty: { message: '密码不能为空' }, identical: { field: 'password', message: '两次密码不一致' }, } } } }; /** * 清除数据 */ UserInfoDlg.clearData = function () { this.userInfoData = {}; }; /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ UserInfoDlg.set = function (key, val) { this.userInfoData[key] = (typeof value == "undefined") ? $("#" + key).val() : value; return this; }; /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ UserInfoDlg.get = function (key) { return $("#" + key).val(); }; /** * 关闭此对话框 */ UserInfoDlg.close = function () { parent.layer.close(window.parent.MgrUser.layerIndex); }; /** * 点击部门input框时 * * @param e * @param treeId * @param treeNode * @returns */ UserInfoDlg.onClickDept = function (e, treeId, treeNode) { $("#citySel").attr("value", instance.getSelectedVal()); $("#deptid").attr("value", treeNode.id); }; /** * 显示部门选择的树 * * @returns */ UserInfoDlg.showDeptSelectTree = function () { var cityObj = $("#citySel"); var cityOffset = $("#citySel").offset(); $("#menuContent").css({ left: cityOffset.left + "px", top: cityOffset.top + cityObj.outerHeight() + "px" }).slideDown("fast"); $("body").bind("mousedown", onBodyDown); }; /** * 显示用户详情部门选择的树 * * @returns */ UserInfoDlg.showInfoDeptSelectTree = function () { var cityObj = $("#citySel"); var cityPosition = $("#citySel").position(); $("#menuContent").css({ left: cityPosition.left + "px", top: cityPosition.top + cityObj.outerHeight() + "px" }).slideDown("fast"); $("body").bind("mousedown", onBodyDown); }; /** * 隐藏部门选择的树 */ UserInfoDlg.hideDeptSelectTree = function () { $("#menuContent").fadeOut("fast"); $("body").unbind("mousedown", onBodyDown);// mousedown当鼠标按下就可以触发,不用弹起 }; /** * 收集数据 */ UserInfoDlg.collectData = function () { this.set('id').set('account').set('sex').set('password').set('avatar') .set('email').set('name').set('birthday').set('rePassword').set('deptid').set('phone'); }; /** * 验证两个密码是否一致 */ UserInfoDlg.validatePwd = function () { var password = this.get("password"); var rePassword = this.get("rePassword"); if (password == rePassword) { return true; } else { return false; } }; /** * 验证数据是否为空 */ UserInfoDlg.validate = function () { $('#userInfoForm').data("bootstrapValidator").resetForm(); $('#userInfoForm').bootstrapValidator('validate'); return $("#userInfoForm").data('bootstrapValidator').isValid(); }; /** * 提交添加用户 */ UserInfoDlg.addSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } if (!this.validatePwd()) { Feng.error("两次密码输入不一致"); return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/mgr/add", function (data) { Feng.success("添加成功!"); window.parent.MgrUser.table.refresh(); UserInfoDlg.close(); }, function (data) { Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.userInfoData); ajax.start(); }; /** * 提交修改 */ UserInfoDlg.editSubmit = function () { this.clearData(); this.collectData(); if (!this.validate()) { return; } //提交信息 var ajax = new $ax(Feng.ctxPath + "/mgr/edit", function (data) { Feng.success("修改成功!"); if (window.parent.MgrUser != undefined) { window.parent.MgrUser.table.refresh(); UserInfoDlg.close(); } }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.userInfoData); ajax.start(); }; /** * 修改密码 */ UserInfoDlg.chPwd = function () { var ajax = new $ax(Feng.ctxPath + "/mgr/changePwd", function (data) { Feng.success("修改成功!"); }, function (data) { Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set("oldPwd"); ajax.set("newPwd"); ajax.set("rePwd"); ajax.start(); }; function onBodyDown(event) { if (!( event.target.id == "menuContent" || $( event.target).parents("#menuContent").length > 0)) { UserInfoDlg.hideDeptSelectTree(); } } var instance = null; var eduOrgInstance = null; $(function () { Feng.initValidator("userInfoForm", UserInfoDlg.validateFields); var ztree = new $ZTree("treeDemo", "/dept/tree"); ztree.bindOnClick(UserInfoDlg.onClickDept); ztree.init(); instance = ztree; //初始化性别选项 $("#sex").val($("#sexValue").val()); // 初始化头像上传 var avatarUp = new $WebUpload("avatar"); avatarUp.setUploadBarId("progressBar"); avatarUp.init(); }); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/.bower.json ================================================ { "name": "jquery.bootgrid", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "keywords": [ "grid", "table", "data", "sorting", "filtering", "UI", "component", "HTML5", "accessibility", "bootstrap" ], "version": "1.3.1", "authors": [ { "name": "Rafael Staib", "email": "me@rafaelstaib.com", "url": "http://www.rafaelstaib.com" } ], "homepage": "http://www.jquery-bootgrid.com", "repository": { "type": "git", "url": "git://github.com/rstaib/jquery-bootgrid.git" }, "license": "MIT", "main": [ "dist/jquery.bootgrid.js", "dist/jquery.bootgrid.css" ], "ignore": [ "build", "demo", "dist/*.nupkg", "dist/*.zip", "docs", "lib", "test", "*.nuspec", ".gitattributes", ".gitignore", ".travis.yml", "bootgrid.jquery.json", "Gruntfile.js", "package.json" ], "dependencies": { "jquery": ">=1.9.0", "bootstrap": ">=3.1.1" }, "_release": "1.3.1", "_resolution": { "type": "version", "tag": "1.3.1", "commit": "ada3cee7feb3259d90f8cbab6d630214cfa16b5e" }, "_source": "git://github.com/rstaib/jquery-bootgrid.git", "_target": "~1.3.1", "_originalSource": "jquery.bootgrid", "_direct": true } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/.npmignore ================================================ build demo dist/*.nupkg dist/*.zip docs lib *.nuspec test .gitattributes .gitignore .travis.yml bootgrid.jquery.json bower.json Gruntfile.js ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/jquery.bootgrid.css ================================================ /*! * jQuery Bootgrid v1.3.1 - 09/11/2015 * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ .bootgrid-header, .bootgrid-footer { margin: 15px 0; } .bootgrid-header a, .bootgrid-footer a { outline: 0; } .bootgrid-header .search, .bootgrid-footer .search { display: inline-block; margin: 0 20px 0 0; vertical-align: middle; width: 180px; } .bootgrid-header .search .glyphicon, .bootgrid-footer .search .glyphicon { top: 0; } .bootgrid-header .search .fa, .bootgrid-footer .search .fa { display: table-cell; } .bootgrid-header .search.search-field::-ms-clear, .bootgrid-footer .search.search-field::-ms-clear, .bootgrid-header .search .search-field::-ms-clear, .bootgrid-footer .search .search-field::-ms-clear { display: none; } .bootgrid-header .pagination, .bootgrid-footer .pagination { margin: 0 !important; } .bootgrid-header .actionBar, .bootgrid-footer .infoBar { text-align: right; } .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu { text-align: left; } .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item { cursor: pointer; display: block; margin: 0; padding: 3px 20px; white-space: nowrap; } .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item:hover, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item:hover, .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item:focus, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item:focus { color: #262626; text-decoration: none; background-color: #f5f5f5; } .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox, .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox { margin: 0 2px 4px 0; vertical-align: middle; } .bootgrid-header .actionBar .btn-group > .btn-group .dropdown-menu .dropdown-item.disabled, .bootgrid-footer .infoBar .btn-group > .btn-group .dropdown-menu .dropdown-item.disabled { cursor: not-allowed; } .bootgrid-table { table-layout: fixed; } .bootgrid-table a { outline: 0; } .bootgrid-table th > .column-header-anchor { color: #333; cursor: not-allowed; display: block; position: relative; text-decoration: none; } .bootgrid-table th > .column-header-anchor.sortable { cursor: pointer; } .bootgrid-table th > .column-header-anchor > .text { display: block; margin: 0 16px 0 0; overflow: hidden; -ms-text-overflow: ellipsis; -o-text-overflow: ellipsis; text-overflow: ellipsis; white-space: nowrap; } .bootgrid-table th > .column-header-anchor > .icon { display: block; position: absolute; right: 0; top: 2px; } .bootgrid-table th:hover, .bootgrid-table th:active { background: #fafafa; } .bootgrid-table td { overflow: hidden; -ms-text-overflow: ellipsis; -o-text-overflow: ellipsis; text-overflow: ellipsis; white-space: nowrap; } .bootgrid-table td.loading, .bootgrid-table td.no-results { background: #fff; text-align: center; } .bootgrid-table th.select-cell, .bootgrid-table td.select-cell { text-align: center; width: 30px; } .bootgrid-table th.select-cell .select-box, .bootgrid-table td.select-cell .select-box { margin: 0; outline: 0; } .table-responsive .bootgrid-table { table-layout: inherit !important; } .table-responsive .bootgrid-table th > .column-header-anchor > .text { overflow: inherit !important; -ms-text-overflow: inherit !important; -o-text-overflow: inherit !important; text-overflow: inherit !important; white-space: inherit !important; } .table-responsive .bootgrid-table td { overflow: inherit !important; -ms-text-overflow: inherit !important; -o-text-overflow: inherit !important; text-overflow: inherit !important; white-space: inherit !important; } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/jquery.bootgrid.fa.js ================================================ /*! * jQuery Bootgrid v1.3.1 - 09/11/2015 * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) { /*jshint validthis: true */ "use strict"; $.extend($.fn.bootgrid.Constructor.defaults.css, { icon: "icon fa", iconColumns: "fa-th-list", iconDown: "fa-sort-desc", iconRefresh: "fa-refresh", iconSearch: "fa-search", iconUp: "fa-sort-asc" }); })(jQuery, window); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bootgrid/jquery.bootgrid.js ================================================ /*! * jQuery Bootgrid v1.3.1 - 09/11/2015 * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) { /*jshint validthis: true */ "use strict"; // GRID INTERNAL FIELDS // ==================== var namespace = ".rs.jquery.bootgrid"; // GRID INTERNAL FUNCTIONS // ===================== function appendRow(row) { var that = this; function exists(item) { return that.identifier && item[that.identifier] === row[that.identifier]; } if (!this.rows.contains(exists)) { this.rows.push(row); return true; } return false; } function findFooterAndHeaderItems(selector) { var footer = (this.footer) ? this.footer.find(selector) : $(), header = (this.header) ? this.header.find(selector) : $(); return $.merge(footer, header); } function getParams(context) { return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : this.cachedParams; } function getRequest() { var request = { current: this.current, rowCount: this.rowCount, sort: this.sortDictionary, searchPhrase: this.searchPhrase }, post = this.options.post; post = ($.isFunction(post)) ? post() : post; return this.options.requestHandler($.extend(true, request, post)); } function getCssSelector(css) { return "." + $.trim(css).replace(/\s+/gm, "."); } function getUrl() { var url = this.options.url; return ($.isFunction(url)) ? url() : url; } function init() { this.element.trigger("initialize" + namespace); loadColumns.call(this); // Loads columns from HTML thead tag this.selection = this.options.selection && this.identifier != null; loadRows.call(this); // Loads rows from HTML tbody tag if ajax is false prepareTable.call(this); renderTableHeader.call(this); renderSearchField.call(this); renderActions.call(this); loadData.call(this); this.element.trigger("initialized" + namespace); } function highlightAppendedRows(rows) { if (this.options.highlightRows) { // todo: implement } } function isVisible(column) { return column.visible; } function loadColumns() { var that = this, firstHeadRow = this.element.find("thead > tr").first(), sorted = false; /*jshint -W018*/ firstHeadRow.children().each(function () { var $this = $(this), data = $this.data(), column = { id: data.columnId, identifier: that.identifier == null && data.identifier || false, converter: that.options.converters[data.converter || data.type] || that.options.converters["string"], text: $this.text(), align: data.align || "left", headerAlign: data.headerAlign || "left", cssClass: data.cssClass || "", headerCssClass: data.headerCssClass || "", formatter: that.options.formatters[data.formatter] || null, order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, searchable: !(data.searchable === false), // default: true sortable: !(data.sortable === false), // default: true visible: !(data.visible === false), // default: true visibleInSelection: !(data.visibleInSelection === false), // default: true width: ($.isNumeric(data.width)) ? data.width + "px" : (typeof(data.width) === "string") ? data.width : null }; that.columns.push(column); if (column.order != null) { that.sortDictionary[column.id] = column.order; } // Prevents multiple identifiers if (column.identifier) { that.identifier = column.id; that.converter = column.converter; } // ensures that only the first order will be applied in case of multi sorting is disabled if (!that.options.multiSort && column.order !== null) { sorted = true; } }); /*jshint +W018*/ } /* response = { current: 1, rowCount: 10, rows: [{}, {}], sort: [{ "columnId": "asc" }], total: 101 } */ function loadData() { var that = this; this.element._bgBusyAria(true).trigger("load" + namespace); showLoading.call(this); function containsPhrase(row) { var column, searchPattern = new RegExp(that.searchPhrase, (that.options.caseSensitive) ? "g" : "gi"); for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && column.visible && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; } } return false; } function update(rows, total) { that.currentRows = rows; setTotals.call(that, total); if (!that.options.keepSelection) { that.selectedRows = []; } renderRows.call(that, rows); renderInfos.call(that); renderPagination.call(that); that.element._bgBusyAria(false).trigger("loaded" + namespace); } if (this.options.ajax) { var request = getRequest.call(this), url = getUrl.call(this); if (url == null || typeof url !== "string" || url.length === 0) { throw new Error("Url setting must be a none empty string or a function that returns one."); } // aborts the previous ajax request if not already finished or failed if (this.xqr) { this.xqr.abort(); } var settings = { url: url, data: request, success: function(response) { that.xqr = null; if (typeof (response) === "string") { response = $.parseJSON(response); } response = that.options.responseHandler(response); that.current = response.current; update(response.rows, response.total); }, error: function (jqXHR, textStatus, errorThrown) { that.xqr = null; if (textStatus !== "abort") { renderNoResultsRow.call(that); // overrides loading mask that.element._bgBusyAria(false).trigger("loaded" + namespace); } } }; settings = $.extend(this.options.ajaxSettings, settings); this.xqr = $.ajax(settings); } else { var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, total = rows.length; if (this.rowCount !== -1) { rows = rows.page(this.current, this.rowCount); } // todo: improve the following comment // setTimeout decouples the initialization so that adding event handlers happens before window.setTimeout(function () { update(rows, total); }, 10); } } function loadRows() { if (!this.options.ajax) { var that = this, rows = this.element.find("tbody > tr"); rows.each(function () { var $this = $(this), cells = $this.children("td"), row = {}; $.each(that.columns, function (i, column) { row[column.id] = column.converter.from(cells.eq(i).text()); }); appendRow.call(that, row); }); setTotals.call(this, this.rows.length); sortRows.call(this); } } function setTotals(total) { this.total = total; this.totalPages = (this.rowCount === -1) ? 1 : Math.ceil(this.total / this.rowCount); } function prepareTable() { var tpl = this.options.templates, wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? this.element.parent() : this.element; this.element.addClass(this.options.css.table); // checks whether there is an tbody element; otherwise creates one if (this.element.children("tbody").length === 0) { this.element.append(tpl.body); } if (this.options.navigation & 1) { this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); wrapper.before(this.header); } if (this.options.navigation & 2) { this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); wrapper.after(this.footer); } } function renderActions() { if (this.options.navigation !== 0) { var css = this.options.css, selector = getCssSelector(css.actions), actionItems = findFooterAndHeaderItems.call(this, selector); if (actionItems.length > 0) { var that = this, tpl = this.options.templates, actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button if (this.options.ajax) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), refresh = $(tpl.actionButton.resolve(getParams.call(this, { content: refreshIcon, text: this.options.labels.refresh }))) .on("click" + namespace, function (e) { // todo: prevent multiple fast clicks (fast click detection) e.stopPropagation(); that.current = 1; loadData.call(that); }); actions.append(refresh); } // Row count selection renderRowCountSelection.call(this, actions); // Column selection renderColumnSelection.call(this, actions); replacePlaceHolder.call(this, actionItems, actions); } } } function renderColumnSelection(actions) { if (this.options.columnSelection && this.columns.length > 1) { var that = this, css = this.options.css, tpl = this.options.templates, icon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconColumns })), dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: icon }))), selector = getCssSelector(css.dropDownItem), checkboxSelector = getCssSelector(css.dropDownItemCheckbox), itemsSelector = getCssSelector(css.dropDownMenuItems); $.each(this.columns, function (i, column) { if (column.visibleInSelection) { var item = $(tpl.actionDropDownCheckboxItem.resolve(getParams.call(that, { name: column.id, label: column.text, checked: column.visible }))) .on("click" + namespace, selector, function (e) { e.stopPropagation(); var $this = $(this), checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { column.visible = checkbox.prop("checked"); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); that.element.find("tbody").empty(); // Fixes an column visualization bug renderTableHeader.call(that); loadData.call(that); } }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); } }); actions.append(dropDown); } } function renderInfos() { if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.infos), infoItems = findFooterAndHeaderItems.call(this, selector); if (infoItems.length > 0) { var end = (this.current * this.rowCount), infos = $(this.options.templates.infos.resolve(getParams.call(this, { end: (this.total === 0 || end === -1 || end > this.total) ? this.total : end, start: (this.total === 0) ? 0 : (end - this.rowCount + 1), total: this.total }))); replacePlaceHolder.call(this, infoItems, infos); } } } function renderNoResultsRow() { var tbody = this.element.children("tbody").first(), tpl = this.options.templates, count = this.columns.where(isVisible).length; if (this.selection) { count = count + 1; } tbody.html(tpl.noResults.resolve(getParams.call(this, { columns: count }))); } function renderPagination() { if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.pagination), paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); if (this.rowCount !== -1 && paginationItems.length > 0) { var tpl = this.options.templates, current = this.current, totalPages = this.totalPages, pagination = $(tpl.pagination.resolve(getParams.call(this))), offsetRight = totalPages - current, offsetLeft = (this.options.padding - current) * -1, startWith = ((offsetRight >= this.options.padding) ? Math.max(offsetLeft, 1) : Math.max((offsetLeft - this.options.padding + offsetRight), 1)), maxCount = this.options.padding * 2 + 1, count = (totalPages >= maxCount) ? maxCount : totalPages; renderPaginationItem.call(this, pagination, "first", "«", "first") ._bgEnableAria(current > 1); renderPaginationItem.call(this, pagination, "prev", "<", "prev") ._bgEnableAria(current > 1); for (var i = 0; i < count; i++) { var pos = i + startWith; renderPaginationItem.call(this, pagination, pos, pos, "page-" + pos) ._bgEnableAria()._bgSelectAria(pos === current); } if (count === 0) { renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) ._bgEnableAria(false)._bgSelectAria(); } renderPaginationItem.call(this, pagination, "next", ">", "next") ._bgEnableAria(totalPages > current); renderPaginationItem.call(this, pagination, "last", "»", "last") ._bgEnableAria(totalPages > current); replacePlaceHolder.call(this, paginationItems, pagination); } } } function renderPaginationItem(list, page, text, markerCss) { var that = this, tpl = this.options.templates, css = this.options.css, values = getParams.call(this, { css: markerCss, text: text, page: page }), item = $(tpl.paginationItem.resolve(values)) .on("click" + namespace, getCssSelector(css.paginationButton), function (e) { e.stopPropagation(); e.preventDefault(); var $this = $(this), parent = $this.parent(); if (!parent.hasClass("active") && !parent.hasClass("disabled")) { var commandList = { first: 1, prev: that.current - 1, next: that.current + 1, last: that.totalPages }; var command = $this.data("page"); that.current = commandList[command] || command; loadData.call(that); } $this.trigger("blur"); }); list.append(item); return item; } function renderRowCountSelection(actions) { var that = this, rowCountList = this.options.rowCount; function getText(value) { return (value === -1) ? that.options.labels.all : value; } if ($.isArray(rowCountList)) { var css = this.options.css, tpl = this.options.templates, dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), menuSelector = getCssSelector(css.dropDownMenu), menuTextSelector = getCssSelector(css.dropDownMenuText), menuItemsSelector = getCssSelector(css.dropDownMenuItems), menuItemSelector = getCssSelector(css.dropDownItemButton); $.each(rowCountList, function (index, value) { var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, { text: getText(value), action: value }))) ._bgSelectAria(value === that.rowCount) .on("click" + namespace, menuItemSelector, function (e) { e.preventDefault(); var $this = $(this), newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { // todo: sophisticated solution needed for calculating which page is selected that.current = 1; // that.rowCount === -1 ---> All that.rowCount = newRowCount; $this.parents(menuItemsSelector).children().each(function () { var $item = $(this), currentRowCount = $item.find(menuItemSelector).data("action"); $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); loadData.call(that); } }); dropDown.find(menuItemsSelector).append(item); }); actions.append(dropDown); } } function renderRows(rows) { if (rows.length > 0) { var that = this, css = this.options.css, tpl = this.options.templates, tbody = this.element.children("tbody").first(), allRowsSelected = true, html = ""; $.each(rows, function (index, row) { var cells = "", rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", rowCss = ""; if (that.selection) { var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), selectBox = tpl.select.resolve(getParams.call(that, { type: "checkbox", value: row[that.identifier], checked: selected })); cells += tpl.cell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); allRowsSelected = (allRowsSelected && selected); if (selected) { rowCss += css.selected; rowAttr += " aria-selected=\"true\""; } } var status = row.status != null && that.options.statusMapping[row.status]; if (status) { rowCss += status; } $.each(that.columns, function (j, column) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : column.converter.to(row[column.id]), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, css: ((column.align === "right") ? css.right : (column.align === "center") ? css.center : css.left) + cssClass, style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); if (rowCss.length > 0) { rowAttr += " class=\"" + rowCss + "\""; } html += tpl.row.resolve(getParams.call(that, { attr: rowAttr, cells: cells })); }); // sets or clears multi selectbox state that.element.find("thead " + getCssSelector(that.options.css.selectBox)) .prop("checked", allRowsSelected); tbody.html(html); registerRowEvents.call(this, tbody); } else { renderNoResultsRow.call(this); } } function registerRowEvents(tbody) { var that = this, selectBoxSelector = getCssSelector(this.options.css.selectBox); if (this.selection) { tbody.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) { e.stopPropagation(); var $this = $(this), id = that.converter.from($this.val()); if ($this.prop("checked")) { that.select([id]); } else { that.deselect([id]); } }); } tbody.off("click" + namespace, "> tr") .on("click" + namespace, "> tr", function(e) { e.stopPropagation(); var $this = $(this), id = (that.identifier == null) ? $this.data("row-id") : that.converter.from($this.data("row-id") + ""), row = (that.identifier == null) ? that.currentRows[id] : that.currentRows.first(function (item) { return item[that.identifier] === id; }); if (that.selection && that.options.rowSelect) { if ($this.hasClass(that.options.css.selected)) { that.deselect([id]); } else { that.select([id]); } } that.element.trigger("click" + namespace, [that.columns, row]); }); } function renderSearchField() { if (this.options.navigation !== 0) { var css = this.options.css, selector = getCssSelector(css.search), searchItems = findFooterAndHeaderItems.call(this, selector); if (searchItems.length > 0) { var that = this, tpl = this.options.templates, timer = null, // fast keyup detection currentValue = "", searchFieldSelector = getCssSelector(css.searchField), search = $(tpl.search.resolve(getParams.call(this))), searchField = (search.is(searchFieldSelector)) ? search : search.find(searchFieldSelector); searchField.on("keyup" + namespace, function (e) { e.stopPropagation(); var newValue = $(this).val(); if (currentValue !== newValue || (e.which === 13 && newValue !== "")) { currentValue = newValue; if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { window.clearTimeout(timer); timer = window.setTimeout(function () { executeSearch.call(that, newValue); }, that.options.searchSettings.delay); } } }); replacePlaceHolder.call(this, searchItems, search); } } } function executeSearch(phrase) { if (this.searchPhrase !== phrase) { this.current = 1; this.searchPhrase = phrase; loadData.call(this); } } function renderTableHeader() { var that = this, headerRow = this.element.find("thead > tr"), css = this.options.css, tpl = this.options.templates, html = "", sorting = this.options.sorting; if (this.selection) { var selectBox = (this.options.multiSelect) ? tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); } $.each(this.columns, function (index, column) { if (column.visible) { var sortOrder = that.sortDictionary[column.id], iconCss = ((sorting && sortOrder && sortOrder === "asc") ? css.iconUp : (sorting && sortOrder && sortOrder === "desc") ? css.iconDown : ""), icon = tpl.icon.resolve(getParams.call(that, { iconCss: iconCss })), align = column.headerAlign, cssClass = (column.headerCssClass.length > 0) ? " " + column.headerCssClass : ""; html += tpl.headerCell.resolve(getParams.call(that, { column: column, icon: icon, sortable: sorting && column.sortable && css.sortable || "", css: ((align === "right") ? css.right : (align === "center") ? css.center : css.left) + cssClass, style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); headerRow.html(html); if (sorting) { var sortingSelector = getCssSelector(css.sortable); headerRow.off("click" + namespace, sortingSelector) .on("click" + namespace, sortingSelector, function (e) { e.preventDefault(); setTableHeaderSortDirection.call(that, $(this)); sortRows.call(that); loadData.call(that); }); } // todo: create a own function for that piece of code if (this.selection && this.options.multiSelect) { var selectBoxSelector = getCssSelector(css.selectBox); headerRow.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) { e.stopPropagation(); if ($(this).prop("checked")) { that.select(); } else { that.deselect(); } }); } } function setTableHeaderSortDirection(element) { var css = this.options.css, iconSelector = getCssSelector(css.icon), columnId = element.data("column-id") || element.parents("th").first().data("column-id"), sortOrder = this.sortDictionary[columnId], icon = element.find(iconSelector); if (!this.options.multiSort) { element.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); this.sortDictionary = {}; } if (sortOrder && sortOrder === "asc") { this.sortDictionary[columnId] = "desc"; icon.removeClass(css.iconUp).addClass(css.iconDown); } else if (sortOrder && sortOrder === "desc") { if (this.options.multiSort) { var newSort = {}; for (var key in this.sortDictionary) { if (key !== columnId) { newSort[key] = this.sortDictionary[key]; } } this.sortDictionary = newSort; icon.removeClass(css.iconDown); } else { this.sortDictionary[columnId] = "asc"; icon.removeClass(css.iconDown).addClass(css.iconUp); } } else { this.sortDictionary[columnId] = "asc"; icon.addClass(css.iconUp); } } function replacePlaceHolder(placeholder, element) { placeholder.each(function (index, item) { // todo: check how append is implemented. Perhaps cloning here is superfluous. $(item).before(element.clone(true)).remove(); }); } function showLoading() { var that = this; window.setTimeout(function() { if (that.element._bgAria("busy") === "true") { var tpl = that.options.templates, thead = that.element.children("thead").first(), tbody = that.element.children("tbody").first(), firstCell = tbody.find("tr > td").first(), padding = (that.element.height() - thead.height()) - (firstCell.height() + 20), count = that.columns.where(isVisible).length; if (that.selection) { count = count + 1; } tbody.html(tpl.loading.resolve(getParams.call(that, { columns: count }))); if (that.rowCount !== -1 && padding > 0) { tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); } } }, 250); } function sortRows() { var sortArray = []; function sort(x, y, current) { current = current || 0; var next = current + 1, item = sortArray[current]; function sortOrder(value) { return (item.order === "asc") ? value : value * -1; } return (x[item.id] > y[item.id]) ? sortOrder(1) : (x[item.id] < y[item.id]) ? sortOrder(-1) : (sortArray.length > next) ? sort(x, y, next) : 0; } if (!this.options.ajax) { var that = this; for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ id: key, order: this.sortDictionary[key] }); } } if (sortArray.length > 0) { this.rows.sort(sort); } } } // GRID PUBLIC CLASS DEFINITION // ==================== /** * Represents the jQuery Bootgrid plugin. * * @class Grid * @constructor * @param element {Object} The corresponding DOM element. * @param options {Object} The options to override default settings. * @chainable **/ var Grid = function(element, options) { this.element = $(element); this.origin = this.element.clone(); this.options = $.extend(true, {}, Grid.defaults, this.element.data(), options); // overrides rowCount explicitly because deep copy ($.extend) leads to strange behaviour var rowCount = this.options.rowCount = this.element.data().rowCount || options.rowCount || this.options.rowCount; this.columns = []; this.current = 1; this.currentRows = []; this.identifier = null; // The first column ID that is marked as identifier this.selection = false; this.converter = null; // The converter for the column that is marked as identifier this.rowCount = ($.isArray(rowCount)) ? rowCount[0] : rowCount; this.rows = []; this.searchPhrase = ""; this.selectedRows = []; this.sortDictionary = {}; this.total = 0; this.totalPages = 0; this.cachedParams = { lbl: this.options.labels, css: this.options.css, ctx: {} }; this.header = null; this.footer = null; this.xqr = null; // todo: implement cache }; /** * An object that represents the default settings. * * @static * @class defaults * @for Grid * @example * // Global approach * $.bootgrid.defaults.selection = true; * @example * // Initialization approach * $("#bootgrid").bootgrid({ selection = true }); **/ Grid.defaults = { navigation: 3, // it's a flag: 0 = none, 1 = top, 2 = bottom, 3 = both (top and bottom) padding: 2, // page padding (pagination) columnSelection: true, rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") /** * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. * * @property selection * @type Boolean * @default false * @for defaults * @since 1.0.0 **/ selection: false, /** * Enables multi selection (`selection` must be set to `true` as well). Default value is `false`. * * @property multiSelect * @type Boolean * @default false * @for defaults * @since 1.0.0 **/ multiSelect: false, /** * Enables entire row click selection (`selection` must be set to `true` as well). Default value is `false`. * * @property rowSelect * @type Boolean * @default false * @for defaults * @since 1.1.0 **/ rowSelect: false, /** * Defines whether the row selection is saved internally on filtering, paging and sorting * (even if the selected rows are not visible). * * @property keepSelection * @type Boolean * @default false * @for defaults * @since 1.1.0 **/ keepSelection: false, highlightRows: false, // highlights new rows (find the page of the first new row) sorting: true, multiSort: false, /** * General search settings to configure the search field behaviour. * * @property searchSettings * @type Object * @for defaults * @since 1.2.0 **/ searchSettings: { /** * The time in milliseconds to wait before search gets executed. * * @property delay * @type Number * @default 250 * @for searchSettings **/ delay: 250, /** * The characters to type before the search gets executed. * * @property characters * @type Number * @default 1 * @for searchSettings **/ characters: 1 }, /** * Defines whether the data shall be loaded via an asynchronous HTTP (Ajax) request. * * @property ajax * @type Boolean * @default false * @for defaults **/ ajax: false, /** * Ajax request settings that shall be used for server-side communication. * All setting except data, error, success and url can be overridden. * For the full list of settings go to http://api.jquery.com/jQuery.ajax/. * * @property ajaxSettings * @type Object * @for defaults * @since 1.2.0 **/ ajaxSettings: { /** * Specifies the HTTP method which shall be used when sending data to the server. * Go to http://api.jquery.com/jQuery.ajax/ for more details. * This setting is overriden for backward compatibility. * * @property method * @type String * @default "POST" * @for ajaxSettings **/ method: "POST" }, /** * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` * that returns a `PlainObject` can be passed. Default value is `{}`. * * @property post * @type Object|Function * @default function (request) { return request; } * @for defaults * @deprecated Use instead `requestHandler` **/ post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") /** * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` * that returns a `String` can be passed. Default value is `""`. * * @property url * @type String|Function * @default "" * @for defaults **/ url: "", // or use function () { return ""; } /** * Defines whether the search is case sensitive or insensitive. * * @property caseSensitive * @type Boolean * @default true * @for defaults * @since 1.1.0 **/ caseSensitive: true, // note: The following properties should not be used via data-api attributes /** * Transforms the JSON request object in what ever is needed on the server-side implementation. * * @property requestHandler * @type Function * @default function (request) { return request; } * @for defaults * @since 1.1.0 **/ requestHandler: function (request) { return request; }, /** * Transforms the response object into the expected JSON response object. * * @property responseHandler * @type Function * @default function (response) { return response; } * @for defaults * @since 1.1.0 **/ responseHandler: function (response) { return response; }, /** * A list of converters. * * @property converters * @type Object * @for defaults * @since 1.0.0 **/ converters: { numeric: { from: function (value) { return +value; }, // converts from string to numeric to: function (value) { return value + ""; } // converts from numeric to string }, string: { // default converter from: function (value) { return value; }, to: function (value) { return value; } } }, /** * Contains all css classes. * * @property css * @type Object * @for defaults **/ css: { actions: "actions btn-group", // must be a unique class name or constellation of class names within the header and footer center: "text-center", columnHeaderAnchor: "column-header-anchor", // must be a unique class name or constellation of class names within the column header cell columnHeaderText: "text", dropDownItem: "dropdown-item", // must be a unique class name or constellation of class names within the actionDropDown, dropDownItemButton: "dropdown-item-button", // must be a unique class name or constellation of class names within the actionDropDown dropDownItemCheckbox: "dropdown-item-checkbox", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenu: "dropdown btn-group", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenuItems: "dropdown-menu pull-right", // must be a unique class name or constellation of class names within the actionDropDown dropDownMenuText: "dropdown-text", // must be a unique class name or constellation of class names within the actionDropDown footer: "bootgrid-footer container-fluid", header: "bootgrid-header container-fluid", icon: "icon glyphicon", iconColumns: "glyphicon-th-list", iconDown: "glyphicon-chevron-down", iconRefresh: "glyphicon-refresh", iconSearch: "glyphicon-search", iconUp: "glyphicon-chevron-up", infos: "infos", // must be a unique class name or constellation of class names within the header and footer, left: "text-left", pagination: "pagination", // must be a unique class name or constellation of class names within the header and footer paginationButton: "button", // must be a unique class name or constellation of class names within the pagination /** * CSS class to select the parent div which activates responsive mode. * * @property responsiveTable * @type String * @default "table-responsive" * @for css * @since 1.1.0 **/ responsiveTable: "table-responsive", right: "text-right", search: "search form-group", // must be a unique class name or constellation of class names within the header and footer searchField: "search-field form-control", selectBox: "select-box", // must be a unique class name or constellation of class names within the entire table selectCell: "select-cell", // must be a unique class name or constellation of class names within the entire table /** * CSS class to highlight selected rows. * * @property selected * @type String * @default "active" * @for css * @since 1.1.0 **/ selected: "active", sortable: "sortable", table: "bootgrid-table table" }, /** * A dictionary of formatters. * * @property formatters * @type Object * @for defaults * @since 1.0.0 **/ formatters: {}, /** * Contains all labels. * * @property labels * @type Object * @for defaults **/ labels: { all: "All", infos: "Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries", loading: "Loading...", noResults: "No results found!", refresh: "Refresh", search: "Search" }, /** * Specifies the mapping between status and contextual classes to color rows. * * @property statusMapping * @type Object * @for defaults * @since 1.2.0 **/ statusMapping: { /** * Specifies a successful or positive action. * * @property 0 * @type String * @for statusMapping **/ 0: "success", /** * Specifies a neutral informative change or action. * * @property 1 * @type String * @for statusMapping **/ 1: "info", /** * Specifies a warning that might need attention. * * @property 2 * @type String * @for statusMapping **/ 2: "warning", /** * Specifies a dangerous or potentially negative action. * * @property 3 * @type String * @for statusMapping **/ 3: "danger" }, /** * Contains all templates. * * @property templates * @type Object * @for defaults **/ templates: { actionButton: "", actionDropDown: "
                                    ", actionDropDownItem: "
                                  • {{ctx.text}}
                                  • ", actionDropDownCheckboxItem: "
                                  • ", actions: "
                                    ", body: "
                                    {{ctx.content}}{{ctx.column.text}}{{ctx.icon}}
                                    {{lbl.loading}}
                                    {{lbl.noResults}}
                                    {{ctx.content}}
                                    {{ctx.content}}{{ctx.column.text}}{{ctx.icon}}
                                    {{lbl.loading}}
                                    {{lbl.noResults}}
                                    {{ctx.content}}
                                    ' + entry.label + '
                                    ' + fragments.join("") + '
                                    '; if (options.legend.container != null) $(options.legend.container).html(table); else { var pos = "", p = options.legend.position, m = options.legend.margin; if (m[0] == null) m = [m, m]; if (p.charAt(0) == "n") pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; else if (p.charAt(0) == "s") pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; if (p.charAt(1) == "e") pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; else if (p.charAt(1) == "w") pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; var legend = $('
                                    ' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
                                    ').appendTo(placeholder); if (options.legend.backgroundOpacity != 0.0) { // put in the transparent background // separately to avoid blended labels and // label boxes var c = options.legend.backgroundColor; if (c == null) { c = options.grid.backgroundColor; if (c && typeof c == "string") c = $.color.parse(c); else c = $.color.extract(legend, 'background-color'); c.a = 1; c = c.toString(); } var div = legend.children(); $('
                                    ').prependTo(legend).css('opacity', options.legend.backgroundOpacity); } } } // interactive features var highlights = [], redrawTimeout = null; // returns the data item the mouse is over, or null if none is found function findNearbyItem(mouseX, mouseY, seriesFilter) { var maxDistance = options.grid.mouseActiveRadius, smallestDistance = maxDistance * maxDistance + 1, item = null, foundPoint = false, i, j, ps; for (i = series.length - 1; i >= 0; --i) { if (!seriesFilter(series[i])) continue; var s = series[i], axisx = s.xaxis, axisy = s.yaxis, points = s.datapoints.points, mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, maxy = maxDistance / axisy.scale; ps = s.datapoints.pointsize; // with inverse transforms, we can't use the maxx/maxy // optimization, sadly if (axisx.options.inverseTransform) maxx = Number.MAX_VALUE; if (axisy.options.inverseTransform) maxy = Number.MAX_VALUE; if (s.lines.show || s.points.show) { for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1]; if (x == null) continue; // For points and lines, the cursor must be within a // certain distance to the data point if (x - mx > maxx || x - mx < -maxx || y - my > maxy || y - my < -maxy) continue; // We have to calculate distances in pixels, not in // data units, because the scales of the axes may be different var dx = Math.abs(axisx.p2c(x) - mouseX), dy = Math.abs(axisy.p2c(y) - mouseY), dist = dx * dx + dy * dy; // we save the sqrt // use <= to ensure last point takes precedence // (last generally means on top of) if (dist < smallestDistance) { smallestDistance = dist; item = [i, j / ps]; } } } if (s.bars.show && !item) { // no other point can be nearby var barLeft, barRight; switch (s.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -s.bars.barWidth; break; default: barLeft = -s.bars.barWidth / 2; } barRight = barLeft + s.bars.barWidth; for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1], b = points[j + 2]; if (x == null) continue; // for a bar graph, the cursor must be inside the bar if (series[i].bars.horizontal ? (mx <= Math.max(b, x) && mx >= Math.min(b, x) && my >= y + barLeft && my <= y + barRight) : (mx >= x + barLeft && mx <= x + barRight && my >= Math.min(b, y) && my <= Math.max(b, y))) item = [i, j / ps]; } } } if (item) { i = item[0]; j = item[1]; ps = series[i].datapoints.pointsize; return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), dataIndex: j, series: series[i], seriesIndex: i }; } return null; } function onMouseMove(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return s["hoverable"] != false; }); } function onMouseLeave(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return false; }); } function onClick(e) { triggerClickHoverEvent("plotclick", e, function (s) { return s["clickable"] != false; }); } // trigger click or hover event (they send the same parameters // so we share their code) function triggerClickHoverEvent(eventname, event, seriesFilter) { var offset = eventHolder.offset(), canvasX = event.pageX - offset.left - plotOffset.left, canvasY = event.pageY - offset.top - plotOffset.top, pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); pos.pageX = event.pageX; pos.pageY = event.pageY; var item = findNearbyItem(canvasX, canvasY, seriesFilter); if (item) { // fill in mouse pos for any listeners out there item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10); } if (options.grid.autoHighlight) { // clear auto-highlights for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.auto == eventname && !(item && h.series == item.series && h.point[0] == item.datapoint[0] && h.point[1] == item.datapoint[1])) unhighlight(h.series, h.point); } if (item) highlight(item.series, item.datapoint, eventname); } placeholder.trigger(eventname, [ pos, item ]); } function triggerRedrawOverlay() { var t = options.interaction.redrawOverlayInterval; if (t == -1) { // skip event queue drawOverlay(); return; } if (!redrawTimeout) redrawTimeout = setTimeout(drawOverlay, t); } function drawOverlay() { redrawTimeout = null; // draw highlights octx.save(); overlay.clear(); octx.translate(plotOffset.left, plotOffset.top); var i, hi; for (i = 0; i < highlights.length; ++i) { hi = highlights[i]; if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point); else drawPointHighlight(hi.series, hi.point); } octx.restore(); executeHooks(hooks.drawOverlay, [octx]); } function highlight(s, point, auto) { if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)); } var i = indexOfHighlight(s, point); if (i == -1) { highlights.push({ series: s, point: point, auto: auto }); triggerRedrawOverlay(); } else if (!auto) highlights[i].auto = false; } function unhighlight(s, point) { if (s == null && point == null) { highlights = []; triggerRedrawOverlay(); return; } if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)); } var i = indexOfHighlight(s, point); if (i != -1) { highlights.splice(i, 1); triggerRedrawOverlay(); } } function indexOfHighlight(s, p) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.series == s && h.point[0] == p[0] && h.point[1] == p[1]) return i; } return -1; } function drawPointHighlight(series, point) { var x = point[0], y = point[1], axisx = series.xaxis, axisy = series.yaxis, highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) return; var pointRadius = series.points.radius + series.points.lineWidth / 2; octx.lineWidth = pointRadius; octx.strokeStyle = highlightColor; var radius = 1.5 * pointRadius; x = axisx.p2c(x); y = axisy.p2c(y); octx.beginPath(); if (series.points.symbol == "circle") octx.arc(x, y, radius, 0, 2 * Math.PI, false); else series.points.symbol(octx, x, y, radius, false); octx.closePath(); octx.stroke(); } function drawBarHighlight(series, point) { var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), fillStyle = highlightColor, barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2; } octx.lineWidth = series.bars.lineWidth; octx.strokeStyle = highlightColor; drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); } function getColorOrGradient(spec, bottom, top, defaultColor) { if (typeof spec == "string") return spec; else { // assume this is a gradient spec; IE currently only // supports a simple vertical gradient properly, so that's // what we support too var gradient = ctx.createLinearGradient(0, top, 0, bottom); for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; if (typeof c != "string") { var co = $.color.parse(defaultColor); if (c.brightness != null) co = co.scale('rgb', c.brightness); if (c.opacity != null) co.a *= c.opacity; c = co.toString(); } gradient.addColorStop(i / (l - 1), c); } return gradient; } } } // Add the plot function to the top level of the jQuery object $.plot = function(placeholder, data, options) { //var t0 = new Date(); var plot = new Plot($(placeholder), data, options, $.plot.plugins); //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); return plot; }; $.plot.version = "0.8.3"; $.plot.plugins = []; // Also add the plot function as a chainable property $.fn.plot = function(data, options) { return this.each(function() { $.plot(this, data, options); }); }; // round to nearby lower multiple of base function floorInBase(n, base) { return base * Math.floor(n / base); } })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot/jquery.flot.resize.js ================================================ /* Flot plugin for automatically redrawing plots as the placeholder resizes. Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. It works by listening for changes on the placeholder div (through the jQuery resize event plugin) - if the size changes, it will redraw the plot. There are no options. If you need to disable the plugin for some plots, you can just fix the size of their placeholders. */ /* Inline dependency: * jQuery resize event - v1.1 - 3/14/2010 * http://benalman.com/projects/jquery-resize-plugin/ * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ (function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); (function ($) { var options = { }; // no options function init(plot) { function onResize() { var placeholder = plot.getPlaceholder(); // somebody might have hidden us and we can't plot // when we don't have the dimensions if (placeholder.width() == 0 || placeholder.height() == 0) return; plot.resize(); plot.setupGrid(); plot.draw(); } function bindEvents(plot, eventHolder) { plot.getPlaceholder().resize(onResize); } function shutdown(plot, eventHolder) { plot.getPlaceholder().unbind("resize", onResize); } plot.hooks.bindEvents.push(bindEvents); plot.hooks.shutdown.push(shutdown); } $.plot.plugins.push({ init: init, options: options, name: 'resize', version: '1.0' }); })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot-orderBars/.bower.json ================================================ { "name": "flot-orderBars", "homepage": "https://github.com/emmerich/flot-orderBars", "_release": "d44ff4f2fe", "_resolution": { "type": "branch", "branch": "master", "commit": "d44ff4f2fec4c8bb1254e59567ee39fa3684372e" }, "_source": "git://github.com/emmerich/flot-orderBars.git", "_target": "*", "_originalSource": "emmerich/flot-orderBars", "_direct": true } ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot-orderBars/README.md ================================================ flot-orderBars ============== Fork of the Flot OrderBars plugin found here: http://www.benjaminbuffet.com/public/js/jquery.flot.orderBars.js Improvements ============ ### Compatability with Flot Stack Plugin The main improvement I've made is compatability with the [Flot Stack plugin](https://github.com/flot/flot/blob/master/jquery.flot.stack.js). To use the 2 together: * Ensure that your data is well formed. Each series should contain a bars object with an order integer, like so: ```javascript var series = []; series.push({ data: [], // your raw data bars: { order: 0 } }); series.push({ data: [], // your raw data bars: { order: 1 } }); ``` * Ensure that the order bars plugin is loaded __before__ the stack plugin. See the example for more information. ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot-orderBars/js/jquery.flot.orderBars.js ================================================ /* * Flot plugin to order bars side by side. * * Released under the MIT license by Benjamin BUFFET, 20-Sep-2010. * Modifications made by Steven Hall , 01-May-2013. * * This plugin is an alpha version. * * To activate the plugin you must specify the parameter "order" for the specific serie : * * $.plot($("#placeholder"), [{ data: [ ... ], bars :{ order = null or integer }]) * * If 2 series have the same order param, they are ordered by the position in the array; * * The plugin adjust the point by adding a value depanding of the barwidth * Exemple for 3 series (barwidth : 0.1) : * * first bar décalage : -0.15 * second bar décalage : -0.05 * third bar décalage : 0.05 * */ // INFO: decalage/decallage is French for gap. It's used to denote the spacing applied to each // bar. (function($){ function init(plot){ var orderedBarSeries; var nbOfBarsToOrder; var borderWidth; var borderWidthInXabsWidth; var pixelInXWidthEquivalent = 1; var isHorizontal = false; // A mapping of order integers to decallage. var decallageByOrder = {}; /* * This method add shift to x values */ function reOrderBars(plot, serie, datapoints){ var shiftedPoints = null; if(serieNeedToBeReordered(serie)){ checkIfGraphIsHorizontal(serie); calculPixel2XWidthConvert(plot); retrieveBarSeries(plot); calculBorderAndBarWidth(serie); if(nbOfBarsToOrder >= 2){ var position = findPosition(serie); var decallage = 0; var centerBarShift = calculCenterBarShift(); // If we haven't already calculated the decallage for this order value, do it. if(typeof decallageByOrder[serie.bars.order] === 'undefined') { if (isBarAtLeftOfCenter(position)){ decallageByOrder[serie.bars.order] = -1*(sumWidth(orderedBarSeries,position-1,Math.floor(nbOfBarsToOrder / 2)-1)) - centerBarShift; }else{ decallageByOrder[serie.bars.order] = sumWidth(orderedBarSeries,Math.ceil(nbOfBarsToOrder / 2),position-2) + centerBarShift + borderWidthInXabsWidth*2; } } // Lookup the decallage based on the series' order value. decallage = decallageByOrder[serie.bars.order]; shiftedPoints = shiftPoints(datapoints,serie,decallage); datapoints.points = shiftedPoints; } } return shiftedPoints; } function serieNeedToBeReordered(serie){ return serie.bars != null && serie.bars.show && serie.bars.order != null; } function calculPixel2XWidthConvert(plot){ var gridDimSize = isHorizontal ? plot.getPlaceholder().innerHeight() : plot.getPlaceholder().innerWidth(); var minMaxValues = isHorizontal ? getAxeMinMaxValues(plot.getData(),1) : getAxeMinMaxValues(plot.getData(),0); var AxeSize = minMaxValues[1] - minMaxValues[0]; pixelInXWidthEquivalent = AxeSize / gridDimSize; } function getAxeMinMaxValues(series,AxeIdx){ var minMaxValues = new Array(); for(var i = 0; i < series.length; i++){ minMaxValues[0] = series[i].data[0][AxeIdx]; minMaxValues[1] = series[i].data[series[i].data.length - 1][AxeIdx]; } return minMaxValues; } function retrieveBarSeries(plot){ orderedBarSeries = findOthersBarsToReOrders(plot.getData()); nbOfBarsToOrder = orderedBarSeries.length; } function findOthersBarsToReOrders(series){ var retSeries = new Array(); var orderValuesSeen = []; for(var i = 0; i < series.length; i++){ if(series[i].bars.order != null && series[i].bars.show && orderValuesSeen.indexOf(series[i].bars.order) < 0){ orderValuesSeen.push(series[i].bars.order); retSeries.push(series[i]); } } return retSeries.sort(sortByOrder); } function sortByOrder(serie1,serie2){ var x = serie1.bars.order; var y = serie2.bars.order; return ((x < y) ? -1 : ((x > y) ? 1 : 0)); } function calculBorderAndBarWidth(serie){ borderWidth = typeof serie.bars.lineWidth !== 'undefined' ? serie.bars.lineWidth : 2; borderWidthInXabsWidth = borderWidth * pixelInXWidthEquivalent; } function checkIfGraphIsHorizontal(serie){ if(serie.bars.horizontal){ isHorizontal = true; } } function findPosition(serie){ var pos = 0 for (var i = 0; i < orderedBarSeries.length; ++i) { if (serie == orderedBarSeries[i]){ pos = i; break; } } return pos+1; } function calculCenterBarShift(){ var width = 0; if(nbOfBarsToOrder%2 != 0) width = (orderedBarSeries[Math.ceil(nbOfBarsToOrder / 2)].bars.barWidth)/2; return width; } function isBarAtLeftOfCenter(position){ return position <= Math.ceil(nbOfBarsToOrder / 2); } function sumWidth(series,start,end){ var totalWidth = 0; for(var i = start; i <= end; i++){ totalWidth += series[i].bars.barWidth+borderWidthInXabsWidth*2; } return totalWidth; } function shiftPoints(datapoints,serie,dx){ var ps = datapoints.pointsize; var points = datapoints.points; var j = 0; for(var i = isHorizontal ? 1 : 0;i < points.length; i += ps){ points[i] += dx; //Adding the new x value in the serie to be abble to display the right tooltip value, //using the index 3 to not overide the third index. serie.data[j][3] = points[i]; j++; } return points; } plot.hooks.processDatapoints.push(reOrderBars); } var options = { series : { bars: {order: null} // or number/string } }; $.plot.plugins.push({ init: init, options: options, name: "orderBars", version: "0.2" }); })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/flot.curvedlines/curvedLines.js ================================================ /* The MIT License Copyright (c) 2011 by Michael Zinsmaier and nergal.dev Copyright (c) 2012 by Thomas Ritou Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* ____________________________________________________ what it is: ____________________________________________________ curvedLines is a plugin for flot, that tries to display lines in a smoother way. This is achieved through adding of more data points. The plugin is a data processor and can thus be used in combination with standard line / point rendering options. => 1) with large data sets you may get trouble => 2) if you want to display the points too, you have to plot them as 2nd data series over the lines => 3) consecutive x data points are not allowed to have the same value Feel free to further improve the code ____________________________________________________ how to use it: ____________________________________________________ var d1 = [[5,5],[7,3],[9,12]]; var options = { series: { curvedLines: { active: true }}}; $.plot($("#placeholder"), [{data: d1, lines: { show: true}, curvedLines: {apply: true}}], options); _____________________________________________________ options: _____________________________________________________ active: bool true => plugin can be used apply: bool true => series will be drawn as curved line monotonicFit: bool true => uses monotone cubic interpolation (preserve monotonicity) tension: int defines the tension parameter of the hermite spline interpolation (no effect if monotonicFit is set) nrSplinePoints: int defines the number of sample points (of the spline) in between two consecutive points deprecated options from flot prior to 1.0.0: ------------------------------------------------ legacyOverride bool true => use old default OR legacyOverride optionArray { fit: bool true => forces the max,mins of the curve to be on the datapoints curvePointFactor int defines how many "virtual" points are used per "real" data point to emulate the curvedLines (points total = real points * curvePointFactor) fitPointDist: int defines the x axis distance of the additional two points that are used } to enforce the min max condition. */ /* * v0.1 initial commit * v0.15 negative values should work now (outcommented a negative -> 0 hook hope it does no harm) * v0.2 added fill option (thanks to monemihir) and multi axis support (thanks to soewono effendi) * v0.3 improved saddle handling and added basic handling of Dates * v0.4 rewritten fill option (thomas ritou) mostly from original flot code (now fill between points rather than to graph bottom), corrected fill Opacity bug * v0.5 rewritten instead of implementing a own draw function CurvedLines is now based on the processDatapoints flot hook (credits go to thomas ritou). * This change breakes existing code however CurvedLines are now just many tiny straight lines to flot and therefore all flot lines options (like gradient fill, * shadow) are now supported out of the box * v0.6 flot 0.8 compatibility and some bug fixes * v0.6.x changed versioning schema * * v1.0.0 API Break marked existing implementation/options as deprecated * v1.1.0 added the new curved line calculations based on hermite splines * v1.1.1 added a rough parameter check to make sure the new options are used */ (function($) { var options = { series : { curvedLines : { active : false, apply : false, monotonicFit : false, tension : 0.5, nrSplinePoints : 20, legacyOverride : undefined } } }; function init(plot) { plot.hooks.processOptions.push(processOptions); //if the plugin is active register processDatapoints method function processOptions(plot, options) { if (options.series.curvedLines.active) { plot.hooks.processDatapoints.unshift(processDatapoints); } } //only if the plugin is active function processDatapoints(plot, series, datapoints) { var nrPoints = datapoints.points.length / datapoints.pointsize; var EPSILON = 0.005; //detects missplaced legacy parameters (prior v1.x.x) in the options object //this can happen if somebody upgrades to v1.x.x without adjusting the parameters or uses old examples var invalidLegacyOptions = hasInvalidParameters(series.curvedLines); if (!invalidLegacyOptions && series.curvedLines.apply == true && series.originSeries === undefined && nrPoints > (1 + EPSILON)) { if (series.lines.fill) { var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1); var pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2); //flot makes sure for us that we've got a second y point if fill is true ! //Merge top and bottom curve datapoints.pointsize = 3; datapoints.points = []; var j = 0; var k = 0; var i = 0; var ps = 2; while (i < pointsTop.length || j < pointsBottom.length) { if (pointsTop[i] == pointsBottom[j]) { datapoints.points[k] = pointsTop[i]; datapoints.points[k + 1] = pointsTop[i + 1]; datapoints.points[k + 2] = pointsBottom[j + 1]; j += ps; i += ps; } else if (pointsTop[i] < pointsBottom[j]) { datapoints.points[k] = pointsTop[i]; datapoints.points[k + 1] = pointsTop[i + 1]; datapoints.points[k + 2] = k > 0 ? datapoints.points[k - 1] : null; i += ps; } else { datapoints.points[k] = pointsBottom[j]; datapoints.points[k + 1] = k > 1 ? datapoints.points[k - 2] : null; datapoints.points[k + 2] = pointsBottom[j + 1]; j += ps; } k += 3; } } else if (series.lines.lineWidth > 0) { datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1); datapoints.pointsize = 2; } } } function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) { if ( typeof curvedLinesOptions.legacyOverride != 'undefined' && curvedLinesOptions.legacyOverride != false) { var defaultOptions = { fit : false, curvePointFactor : 20, fitPointDist : undefined }; var legacyOptions = jQuery.extend(defaultOptions, curvedLinesOptions.legacyOverride); return calculateLegacyCurvePoints(datapoints, legacyOptions, yPos); } return calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos); } function calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos) { var points = datapoints.points; var ps = datapoints.pointsize; //create interpolant fuction var splines = createHermiteSplines(datapoints, curvedLinesOptions, yPos); var result = []; //sample the function // (the result is intependent from the input data => // it is ok to alter the input after this method) var j = 0; for (var i = 0; i < points.length - ps; i += ps) { var curX = i; var curY = i + yPos; var xStart = points[curX]; var xEnd = points[curX + ps]; var xStep = (xEnd - xStart) / Number(curvedLinesOptions.nrSplinePoints); //add point result.push(points[curX]); result.push(points[curY]); //add curve point for (var x = (xStart += xStep); x < xEnd; x += xStep) { result.push(x); result.push(splines[j](x)); } j++; } //add last point result.push(points[points.length - ps]); result.push(points[points.length - ps + yPos]); return result; } // Creates an array of splines, one for each segment of the original curve. Algorithm based on the wikipedia articles: // // http://de.wikipedia.org/w/index.php?title=Kubisch_Hermitescher_Spline&oldid=130168003 and // http://en.wikipedia.org/w/index.php?title=Monotone_cubic_interpolation&oldid=622341725 and the description of Fritsch-Carlson from // http://math.stackexchange.com/questions/45218/implementation-of-monotone-cubic-interpolation // for a detailed description see https://github.com/MichaelZinsmaier/CurvedLines/docu function createHermiteSplines(datapoints, curvedLinesOptions, yPos) { var points = datapoints.points; var ps = datapoints.pointsize; // preparation get length (x_{k+1} - x_k) and slope s=(p_{k+1} - p_k) / (x_{k+1} - x_k) of the segments var segmentLengths = []; var segmentSlopes = []; for (var i = 0; i < points.length - ps; i += ps) { var curX = i; var curY = i + yPos; var dx = points[curX + ps] - points[curX]; var dy = points[curY + ps] - points[curY]; segmentLengths.push(dx); segmentSlopes.push(dy / dx); } //get the values for the desired gradients m_k for all points k //depending on the used method the formula is different var gradients = [segmentSlopes[0]]; if (curvedLinesOptions.monotonicFit) { // Fritsch Carlson for (var i = 1; i < segmentLengths.length; i++) { var slope = segmentSlopes[i]; var prev_slope = segmentSlopes[i - 1]; if (slope * prev_slope <= 0) { // sign(prev_slope) != sign(slpe) gradients.push(0); } else { var length = segmentLengths[i]; var prev_length = segmentLengths[i - 1]; var common = length + prev_length; //m = 3 (prev_length + length) / ((2 length + prev_length) / prev_slope + (length + 2 prev_length) / slope) gradients.push(3 * common / ((common + length) / prev_slope + (common + prev_length) / slope)); } } } else { // Cardinal spline with t € [0,1] // Catmull-Rom for t = 0 for (var i = ps; i < points.length - ps; i += ps) { var curX = i; var curY = i + yPos; gradients.push(Number(curvedLinesOptions.tension) * (points[curY + ps] - points[curY - ps]) / (points[curX + ps] - points[curX - ps])); } } gradients.push(segmentSlopes[segmentSlopes.length - 1]); //get the two major coefficients (c'_{oef1} and c'_{oef2}) for each segment spline var coefs1 = []; var coefs2 = []; for (i = 0; i < segmentLengths.length; i++) { var m_k = gradients[i]; var m_k_plus = gradients[i + 1]; var slope = segmentSlopes[i]; var invLength = 1 / segmentLengths[i]; var common = m_k + m_k_plus - slope - slope; coefs1.push(common * invLength * invLength); coefs2.push((slope - common - m_k) * invLength); } //create functions with from the coefficients and capture the parameters var ret = []; for (var i = 0; i < segmentLengths.length; i ++) { var spline = function (x_k, coef1, coef2, coef3, coef4) { // spline for a segment return function (x) { var diff = x - x_k; var diffSq = diff * diff; return coef1 * diff * diffSq + coef2 * diffSq + coef3 * diff + coef4; }; }; ret.push(spline(points[i * ps], coefs1[i], coefs2[i], gradients[i], points[i * ps + yPos])); } return ret; }; //no real idea whats going on here code mainly from https://code.google.com/p/flot/issues/detail?id=226 //if fit option is selected additional datapoints get inserted before the curve calculations in nergal.dev s code. function calculateLegacyCurvePoints(datapoints, curvedLinesOptions, yPos) { var points = datapoints.points; var ps = datapoints.pointsize; var num = Number(curvedLinesOptions.curvePointFactor) * (points.length / ps); var xdata = new Array; var ydata = new Array; var curX = -1; var curY = -1; var j = 0; if (curvedLinesOptions.fit) { //insert a point before and after the "real" data point to force the line //to have a max,min at the data point. var fpDist; if ( typeof curvedLinesOptions.fitPointDist == 'undefined') { //estimate it var minX = points[0]; var maxX = points[points.length - ps]; fpDist = (maxX - minX) / (500 * 100); //x range / (estimated pixel length of placeholder * factor) } else { //use user defined value fpDist = Number(curvedLinesOptions.fitPointDist); } for (var i = 0; i < points.length; i += ps) { var frontX; var backX; curX = i; curY = i + yPos; //add point X s frontX = points[curX] - fpDist; backX = points[curX] + fpDist; var factor = 2; while (frontX == points[curX] || backX == points[curX]) { //inside the ulp frontX = points[curX] - (fpDist * factor); backX = points[curX] + (fpDist * factor); factor++; } //add curve points xdata[j] = frontX; ydata[j] = points[curY]; j++; xdata[j] = points[curX]; ydata[j] = points[curY]; j++; xdata[j] = backX; ydata[j] = points[curY]; j++; } } else { //just use the datapoints for (var i = 0; i < points.length; i += ps) { curX = i; curY = i + yPos; xdata[j] = points[curX]; ydata[j] = points[curY]; j++; } } var n = xdata.length; var y2 = new Array(); var delta = new Array(); y2[0] = 0; y2[n - 1] = 0; delta[0] = 0; for (var i = 1; i < n - 1; ++i) { var d = (xdata[i + 1] - xdata[i - 1]); if (d == 0) { //point before current point and after current point need some space in between return []; } var s = (xdata[i] - xdata[i - 1]) / d; var p = s * y2[i - 1] + 2; y2[i] = (s - 1) / p; delta[i] = (ydata[i + 1] - ydata[i]) / (xdata[i + 1] - xdata[i]) - (ydata[i] - ydata[i - 1]) / (xdata[i] - xdata[i - 1]); delta[i] = (6 * delta[i] / (xdata[i + 1] - xdata[i - 1]) - s * delta[i - 1]) / p; } for (var j = n - 2; j >= 0; --j) { y2[j] = y2[j] * y2[j + 1] + delta[j]; } // xmax - xmin / #points var step = (xdata[n - 1] - xdata[0]) / (num - 1); var xnew = new Array; var ynew = new Array; var result = new Array; xnew[0] = xdata[0]; ynew[0] = ydata[0]; result.push(xnew[0]); result.push(ynew[0]); for ( j = 1; j < num; ++j) { //new x point (sampling point for the created curve) xnew[j] = xnew[0] + j * step; var max = n - 1; var min = 0; while (max - min > 1) { var k = Math.round((max + min) / 2); if (xdata[k] > xnew[j]) { max = k; } else { min = k; } } //found point one to the left and one to the right of generated new point var h = (xdata[max] - xdata[min]); if (h == 0) { //similar to above two points from original x data need some space between them return []; } var a = (xdata[max] - xnew[j]) / h; var b = (xnew[j] - xdata[min]) / h; ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6; result.push(xnew[j]); result.push(ynew[j]); } return result; } function hasInvalidParameters(curvedLinesOptions) { if (typeof curvedLinesOptions.fit != 'undefined' || typeof curvedLinesOptions.curvePointFactor != 'undefined' || typeof curvedLinesOptions.fitPointDist != 'undefined') { throw new Error("CurvedLines detected illegal parameters. The CurvedLines API changed with version 1.0.0 please check the options object."); return true; } return false; } }//end init $.plot.plugins.push({ init : init, options : options, name : 'curvedLines', version : '1.1.1' }); })(jQuery); ================================================ FILE: material-manage/src/main/webapp/static/vendors/bower_components/jquery/2.1.4/jquery.js ================================================ /*! * jQuery JavaScript Library v2.1.4 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2015-04-28T16:01Z */ (function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { // For CommonJS and CommonJS-like environments where a proper `window` // is present, execute the factory and get jQuery. // For environments that do not have a `window` with a `document` // (such as Node.js), expose a factory as module.exports. // This accentuates the need for the creation of a real `window`. // e.g. var jQuery = require("jquery")(window); // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // Support: Firefox 18+ // Can't be in strict mode, several libs including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) // var arr = []; var slice = arr.slice; var concat = arr.concat; var push = arr.push; var indexOf = arr.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var support = {}; var // Use the correct document accordingly with window argument (sandbox) document = window.document, version = "2.1.4", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }, // Support: Android<4.1 // Make sure we trim BOM and NBSP rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, toArray: function() { return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num != null ? // Return just the one element from the set ( num < 0 ? this[ num + this.length ] : this[ num ] ) : // Return all the elements in a clean array slice.call( this ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, slice: function() { return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }, end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: arr.sort, splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module isReady: true, error: function( msg ) { throw new Error( msg ); }, noop: function() {}, isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray, isWindow: function( obj ) { return obj != null && obj === obj.window; }, isNumeric: function( obj ) { // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN // adding 1 corrects loss of precision from parseFloat (#15100) return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; }, isPlainObject: function( obj ) { // Not plain objects: // - Any object or value whose internal [[Class]] property is not "[object Object]" // - DOM nodes // - window if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } if ( obj.constructor && !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } // If the function hasn't returned already, we're confident that // |obj| is a plain object, created by {} or constructed with new Object return true; }, isEmptyObject: function( obj ) { var name; for ( name in obj ) { return false; } return true; }, type: function( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android<4.0, iOS<6 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call(obj) ] || "object" : typeof obj; }, // Evaluates a script in a global context globalEval: function( code ) { var script, indirect = eval; code = jQuery.trim( code ); if ( code ) { // If the code includes a valid, prologue position // strict mode pragma, execute code by injecting a // script tag into the document. if ( code.indexOf("use strict") === 1 ) { script = document.createElement("script"); script.text = code; document.head.appendChild( script ).parentNode.removeChild( script ); } else { // Otherwise, avoid the DOM node creation, insertion // and removal by using an indirect global eval indirect( code ); } } }, // Convert dashed to camelCase; used by the css and data modules // Support: IE9-11+ // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); if ( args ) { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } } return obj; }, // Support: Android<4.1 trim: function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArraylike( Object(arr) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { return arr == null ? -1 : indexOf.call( arr, elem, i ); }, merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }, grep: function( elems, callback, invert ) { var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { callbackInverse = !callback( elems[ i ], i ); if ( callbackInverse !== callbackExpect ) { matches.push( elems[ i ] ); } } return matches; }, // arg is for internal usage only map: function( elems, callback, arg ) { var value, i = 0, length = elems.length, isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their new values if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } } // Flatten any nested arrays return concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { var tmp, args, proxy; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, now: Date.now, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support }); // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); function isArraylike( obj ) { // Support: iOS 8.2 (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } if ( obj.nodeType === 1 && length ) { return true; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! * Sizzle CSS Selector Engine v2.2.0-pre * http://sizzlejs.com/ * * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2014-12-16 */ (function( window ) { var i, support, Expr, getText, isXML, tokenize, compile, select, outermostContext, sortInput, hasDuplicate, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; }, // General-purpose constants MAX_NEGATIVE = 1 << 31, // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native // http://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; for ( ; i < len; i++ ) { if ( list[i] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/css3-syntax/#characters characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = characterEncoding.replace( "w", "w#" ), // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + characterEncoding + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) ".*" + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + characterEncoding + ")" ), "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); }; // Optimize for push.apply( _, NodeList ) try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // Leverage slice if possible function( target, els ) { push_native.apply( target, slice.call(els) ); } : // Support: IE<9 // Otherwise append directly function( target, els ) { var j = target.length, i = 0; // Can't trust NodeList.length while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var match, elem, m, nodeType, // QSA vars i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; results = results || []; nodeType = context.nodeType; if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } if ( !seed && documentIsHTML ) { // Try to shortcut find operations when possible (e.g., not under DocumentFragment) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document (jQuery #6963) if ( elem && elem.parentNode ) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } } else { // Context is not a document if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Speed-up: Sizzle("TAG") } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Speed-up: Sizzle(".CLASS") } else if ( (m = match[3]) && support.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // QSA path if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType !== 1 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] "; i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size * @returns {Function(string, Object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key + " " ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created div and expects a boolean result */ function assert( fn ) { var div = document.createElement("div"); try { return !!fn( div ); } catch (e) { return false; } finally { // Remove from its parent by default if ( div.parentNode ) { div.parentNode.removeChild( div ); } // release memory in IE div = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), i = attrs.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, parent, doc = node ? node.ownerDocument || node : preferredDoc; // If no document and documentElement is available, return if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Set our document document = doc; docElem = doc.documentElement; parent = doc.defaultView; // Support: IE>8 // If iframe document is assigned to "document" variable and if iframe has been reloaded, // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 // IE6-8 do not support the defaultView property so parent will be undefined if ( parent && parent !== parent.top ) { // IE11 does not have attachEvent, so all must suffer if ( parent.addEventListener ) { parent.addEventListener( "unload", unloadHandler, false ); } else if ( parent.attachEvent ) { parent.attachEvent( "onunload", unloadHandler ); } } /* Support tests ---------------------------------------------------------------------- */ documentIsHTML = !isXML( doc ); /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // (excepting IE8 booleans) support.attributes = assert(function( div ) { div.className = "i"; return !div.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( div ) { div.appendChild( doc.createComment("") ); return !div.getElementsByTagName("*").length; }); // Support: IE<9 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programatically-set names, // so use a roundabout getElementsByName test support.getById = assert(function( div ) { docElem.appendChild( div ).id = expando; return !doc.getElementsByName || !doc.getElementsByName( expando ).length; }); // ID find and filter if ( support.getById ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [ m ] : []; } }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; } else { // Support: IE6/7 // getElementById is not reliable as a find shortcut delete Expr.find["ID"]; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); // DocumentFragment nodes don't have gEBTN } else if ( support.qsa ) { return context.querySelectorAll( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 docElem.appendChild( div ).innerHTML = "" + ""; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section if ( div.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !div.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 // In-page `selector#id sibing-combinator selector` fails if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { rbuggyQSA.push(".#.+[+~]"); } }); assert(function( div ) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( div.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( div ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( div, "div" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( div, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } // Calculate position if both inputs belong to the same document compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : // Otherwise we know they are disconnected 1; // Disconnected nodes if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } // Maintain original order return sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } : function( a, b ) { // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return doc; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } // Make sure that attribute selectors are quoted expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch (e) {} } return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( (elem = results[i++]) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[3] ) { Sizzle.error( match[0] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[6] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is if ( match[3] ) { match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) (excess = tokenize( unquoted, true )) && // advance to the next closing parenthesis (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index outerCache = parent[ expando ] || (parent[ expando ] = {}); cache = outerCache[ type ] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } // Use previously-cached element index if available } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { // Use the same loop as above to seek `elem` from the start while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); // Don't keep the element (issue #299) input[0] = null; return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": function( elem ) { return elem.disabled === false; }, "disabled": function( elem ) { return elem.disabled === true; }, "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeType < 6 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } // Easy API for creating new setFilters function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( (tokens = []) ); } matched = false; // Combinators if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); }; function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var oldCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); if ( (oldCache = outerCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements outerCache[ dir ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } } } } }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // Restore matcherIn since elem is not yet a final match temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); // Avoid hanging onto element (issue #299) checkContext = null; return ret; } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if ( outermost ) { outermostContext = context !== document && context; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context, xml ) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( (elem = !matcher && elem) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // Apply set filters to unmatched elements matchedCount += i; if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !match ) { match = tokenize( selector ); } i = match.length; while ( i-- ) { cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); // Save selector and tokenization cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( (selector = compiled.selector || selector) ); results = results || []; // Try to minimize operations if there is no seed and only one group if ( match.length === 1 ) { // Take a shortcut and set the context if the root selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { return results; // Precompiled matchers will still verify ancestry, so step up a level } else if ( compiled ) { context = context.parentNode; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; // Abort if we hit a combinator if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { // Search, expanding context for leading sibling combinators if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context )) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; // One-time assignments // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert(function( div1 ) { // Should return 1, but returns 4 (following) return div1.compareDocumentPosition( document.createElement("div") ) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !assert(function( div ) { div.innerHTML = ""; return div.firstChild.getAttribute("href") === "#" ; }) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") if ( !support.attributes || !assert(function( div ) { div.innerHTML = ""; div.firstChild.setAttribute( "value", "" ); return div.firstChild.getAttribute( "value" ) === ""; }) ) { addHandle( "value", function( elem, name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies if ( !assert(function( div ) { return div.getAttribute("disabled") == null; }) ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { return elem[ name ] === true ? name.toLowerCase() : (val = elem.getAttributeNode( name )) && val.specified ? val.value : null; } }); } return Sizzle; })( window ); jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; var rneedsContext = jQuery.expr.match.needsContext; var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; }); } if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; }); } if ( typeof qualifier === "string" ) { if ( risSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } qualifier = jQuery.filter( qualifier, elements ); } return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; }); } jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 && elem.nodeType === 1 ? jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; })); }; jQuery.fn.extend({ find: function( selector ) { var i, len = this.length, ret = [], self = this; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }) ); } for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } // Needed because $( selector, context ) becomes $( context ).find( selector ) ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); ret.selector = this.selector ? this.selector + " " + selector : selector; return ret; }, filter: function( selector ) { return this.pushStack( winnow(this, selector || [], false) ); }, not: function( selector ) { return this.pushStack( winnow(this, selector || [], true) ); }, is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; } }); // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, init = jQuery.fn.init = function( selector, context ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings if ( typeof selector === "string" ) { if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[2] ); // Support: Blackberry 4.6 // gEBID returns nodes no longer in the document (#6963) if ( elem && elem.parentNode ) { // Inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.extend({ dir: function( elem, dir, until ) { var matched = [], truncate = until !== undefined; while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { if ( elem.nodeType === 1 ) { if ( truncate && jQuery( elem ).is( until ) ) { break; } matched.push( elem ); } } return matched; }, sibling: function( n, elem ) { var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { matched.push( n ); } } return matched; } }); jQuery.fn.extend({ has: function( target ) { var targets = jQuery( target, this ), l = targets.length; return this.filter(function() { var i = 0; for ( ; i < l; i++ ) { if ( jQuery.contains( this, targets[i] ) ) { return true; } } }); }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, matched = [], pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? jQuery( selectors, context || this.context ) : 0; for ( ; i < l; i++ ) { for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { // Always skip document fragments if ( cur.nodeType < 11 && (pos ? pos.index(cur) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors)) ) { matched.push( cur ); break; } } } return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); }, // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // Index in selector if ( typeof elem === "string" ) { return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element return indexOf.call( this, // If it receives a jQuery object, the first element is used elem.jquery ? elem[ 0 ] : elem ); }, add: function( selector, context ) { return this.pushStack( jQuery.unique( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter(selector) ); } }); function sibling( cur, dir ) { while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} return cur; } jQuery.each({ parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { return elem.contentDocument || jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { jQuery.unique( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { matched.reverse(); } } return this.pushStack( matched ); }; }); var rnotwhite = (/\S+/g); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; jQuery.extend({ Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, resolveValues = slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // Add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // If we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } }); // The deferred used on DOM ready var readyList; jQuery.fn.ready = function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }; jQuery.extend({ // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.triggerHandler ) { jQuery( document ).triggerHandler( "ready" ); jQuery( document ).off( "ready" ); } } }); /** * The ready event handler and self cleanup method */ function completed() { document.removeEventListener( "DOMContentLoaded", completed, false ); window.removeEventListener( "load", completed, false ); jQuery.ready(); } jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred. // We once tried to use readyState "interactive" here, but it caused issues like the one // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); } else { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed, false ); } } return readyList.promise( obj ); }; // Kick off the DOM ready check even if the user does not jQuery.ready.promise(); // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : len ? fn( elems[0], key ) : emptyGet; }; /** * Determines whether an object can have data */ jQuery.acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any /* jshint -W018 */ return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; function Data() { // Support: Android<4, // Old WebKit does not have Object.preventExtensions/freeze method, // return new empty object instead with no [[set]] accessor Object.defineProperty( this.cache = {}, 0, { get: function() { return {}; } }); this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; Data.accepts = jQuery.acceptData; Data.prototype = { key: function( owner ) { // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return the key for a frozen object. if ( !Data.accepts( owner ) ) { return 0; } var descriptor = {}, // Check if the owner object already has a cache key unlock = owner[ this.expando ]; // If not, create one if ( !unlock ) { unlock = Data.uid++; // Secure it in a non-enumerable, non-writable property try { descriptor[ this.expando ] = { value: unlock }; Object.defineProperties( owner, descriptor ); // Support: Android<4 // Fallback to a less secure definition } catch ( e ) { descriptor[ this.expando ] = unlock; jQuery.extend( owner, descriptor ); } } // Ensure the cache object if ( !this.cache[ unlock ] ) { this.cache[ unlock ] = {}; } return unlock; }, set: function( owner, data, value ) { var prop, // There may be an unlock assigned to this node, // if there is no entry for this "owner", create one inline // and set the unlock as though an owner entry had always existed unlock = this.key( owner ), cache = this.cache[ unlock ]; // Handle: [ owner, key, value ] args if ( typeof data === "string" ) { cache[ data ] = value; // Handle: [ owner, { properties } ] args } else { // Fresh assignments by object are shallow copied if ( jQuery.isEmptyObject( cache ) ) { jQuery.extend( this.cache[ unlock ], data ); // Otherwise, copy the properties one-by-one to the cache object } else { for ( prop in data ) { cache[ prop ] = data[ prop ]; } } } return cache; }, get: function( owner, key ) { // Either a valid cache is found, or will be created. // New caches will be created and the unlock returned, // allowing direct access to the newly created // empty data object. A valid owner object must be provided. var cache = this.cache[ this.key( owner ) ]; return key === undefined ? cache : cache[ key ]; }, access: function( owner, key, value ) { var stored; // In cases where either: // // 1. No key was specified // 2. A string key was specified, but no value provided // // Take the "read" path and allow the get method to determine // which value to return, respectively either: // // 1. The entire cache object // 2. The data stored at the key // if ( key === undefined || ((key && typeof key === "string") && value === undefined) ) { stored = this.get( owner, key ); return stored !== undefined ? stored : this.get( owner, jQuery.camelCase(key) ); } // [*]When the key is not a string, or both a key and value // are specified, set or extend (existing objects) with either: // // 1. An object of properties // 2. A key and value // this.set( owner, key, value ); // Since the "set" path can have two possible entry points // return the expected data based on which path was taken[*] return value !== undefined ? value : key; }, remove: function( owner, key ) { var i, name, camel, unlock = this.key( owner ), cache = this.cache[ unlock ]; if ( key === undefined ) { this.cache[ unlock ] = {}; } else { // Support array or space separated string of keys if ( jQuery.isArray( key ) ) { // If "name" is an array of keys... // When data is initially created, via ("key", "val") signature, // keys will be converted to camelCase. // Since there is no way to tell _how_ a key was added, remove // both plain key and camelCase key. #12786 // This will only penalize the array argument path. name = key.concat( key.map( jQuery.camelCase ) ); } else { camel = jQuery.camelCase( key ); // Try the string as a key before any manipulation if ( key in cache ) { name = [ key, camel ]; } else { // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace name = camel; name = name in cache ? [ name ] : ( name.match( rnotwhite ) || [] ); } } i = name.length; while ( i-- ) { delete cache[ name[ i ] ]; } } }, hasData: function( owner ) { return !jQuery.isEmptyObject( this.cache[ owner[ this.expando ] ] || {} ); }, discard: function( owner ) { if ( owner[ this.expando ] ) { delete this.cache[ owner[ this.expando ] ]; } } }; var data_priv = new Data(); var data_user = new Data(); // Implementation Summary // // 1. Enforce API surface and semantic compatibility with 1.9.x branch // 2. Improve the module's maintainability by reducing the storage // paths to a single mechanism. // 3. Use the same single mechanism to support "private" and "user" data. // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 5. Avoid exposing implementation details on user objects (eg. expando properties) // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /([A-Z])/g; function dataAttr( elem, key, data ) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later data_user.set( elem, key, data ); } else { data = undefined; } } return data; } jQuery.extend({ hasData: function( elem ) { return data_user.hasData( elem ) || data_priv.hasData( elem ); }, data: function( elem, name, data ) { return data_user.access( elem, name, data ); }, removeData: function( elem, name ) { data_user.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to data_priv methods, these can be deprecated. _data: function( elem, name, data ) { return data_priv.access( elem, name, data ); }, _removeData: function( elem, name ) { data_priv.remove( elem, name ); } }); jQuery.fn.extend({ data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { if ( this.length ) { data = data_user.get( elem ); if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE11+ // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice(5) ); dataAttr( elem, name, data[ name ] ); } } } data_priv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each(function() { data_user.set( this, key ); }); } return access( this, function( value ) { var data, camelKey = jQuery.camelCase( key ); // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // with the key as-is data = data_user.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to get data from the cache // with the key camelized data = data_user.get( elem, camelKey ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, camelKey, undefined ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each(function() { // First, attempt to store a copy or reference of any // data that might've been store with a camelCased key. var data = data_user.get( this, camelKey ); // For HTML5 data-* attribute interop, we have to // store property names with dashes in a camelCase form. // This might not apply to all properties...* data_user.set( this, camelKey, value ); // *... In the case of properties that might _actually_ // have dashes, we need to also store a copy of that // unchanged property. if ( key.indexOf("-") !== -1 && data !== undefined ) { data_user.set( this, key, value ); } }); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each(function() { data_user.remove( this, key ); }); } }); jQuery.extend({ queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = data_priv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || jQuery.isArray( data ) ) { queue = data_priv.access( elem, type, jQuery.makeArray(data) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return data_priv.get( elem, key ) || data_priv.access( elem, key, { empty: jQuery.Callbacks("once memory").add(function() { data_priv.remove( elem, [ type + "queue", key ] ); }) }); } }); jQuery.fn.extend({ queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } return data === undefined ? this : this.each(function() { var queue = jQuery.queue( this, type, data ); // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while ( i-- ) { tmp = data_priv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } }); var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHidden = function( elem, el ) { // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); }; var rcheckableType = (/^(?:checkbox|radio)$/i); (function() { var fragment = document.createDocumentFragment(), div = fragment.appendChild( document.createElement( "div" ) ), input = document.createElement( "input" ); // Support: Safari<=5.1 // Check state lost if the name is set (#11217) // Support: Windows Web Apps (WWA) // `name` and `type` must use .setAttribute for WWA (#14901) input.setAttribute( "type", "radio" ); input.setAttribute( "checked", "checked" ); input.setAttribute( "name", "t" ); div.appendChild( input ); // Support: Safari<=5.1, Android<4.2 // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE<=11+ // Make sure textarea (and checkbox) defaultValue is properly cloned div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; })(); var strundefined = typeof undefined; support.focusinBubbles = "onfocusin" in window; var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; function returnTrue() { return true; } function returnFalse() { return false; } function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.get( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !(events = elemData.events) ) { events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; } // Handle multiple events separated by a space types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); // Init the event handler queue if we're the first if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.hasData( elem ) && data_priv.get( elem ); if ( !elemData || !(events = elemData.events) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { delete elemData.handle; data_priv.remove( elem, "events" ); } }, trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && handle.apply && jQuery.acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); var i, j, ret, matched, handleObj, handlerQueue = [], args = slice.call( arguments ), handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or 2) have namespace(s) // a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); if ( ret !== undefined ) { if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var i, matches, sel, handleObj, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers // Black-hole SVG instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.disabled !== true || event.type !== "click" ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) { matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) { handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; }, // Includes some event props shared by KeyEvent and MouseEvent props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function( event, original ) { // Add which for key events if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { var eventDoc, doc, body, button = original.button; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, copy, type = event.type, originalEvent = event, fixHook = this.fixHooks[ type ]; if ( !fixHook ) { this.fixHooks[ type ] = fixHook = rmouseEvent.test( type ) ? this.mouseHooks : rkeyEvent.test( type ) ? this.keyHooks : {}; } copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; event = new jQuery.Event( originalEvent ); i = copy.length; while ( i-- ) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; } // Support: Cordova 2.5 (WebKit) (#13255) // All events should have a target; Cordova deviceready doesn't if ( !event.target ) { event.target = document; } // Support: Safari 6.0+, Chrome<28 // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { this.focus(); return false; } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === safeActiveElement() && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right trigger: function() { if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links _default: function( event ) { return jQuery.nodeName( event.target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } } }, simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if ( bubble ) { jQuery.event.trigger( e, null, elem ); } else { jQuery.event.dispatch.call( elem, e ); } if ( e.isDefaultPrevented() ) { event.preventDefault(); } } }; jQuery.removeEvent = function( elem, type, handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android<4.0 src.returnValue === false ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( e && e.preventDefault ) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( e && e.stopPropagation ) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if ( e && e.stopImmediatePropagation ) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; // Create mouseenter/leave events using mouseover/out and event-time checks // Support: Chrome 15+ jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); // Support: Firefox, Chrome, Safari // Create "bubbling" focus and blur events if ( !support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { var doc = this.ownerDocument || this, attaches = data_priv.access( doc, fix ); if ( !attaches ) { doc.addEventListener( orig, handler, true ); } data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { var doc = this.ownerDocument || this, attaches = data_priv.access( doc, fix ) - 1; if ( !attaches ) { doc.removeEventListener( orig, handler, true ); data_priv.remove( doc, fix ); } else { data_priv.access( doc, fix, attaches ); } } }; }); } jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }, one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); }); }, trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } }); var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rhtml = /<|&#?\w+;/, rnoInnerhtml = /<(?:script|style|link)/i, // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /^$|\/(?:java|ecma)script/i, rscriptTypeMasked = /^true\/(.*)/, rcleanScript = /^\s*\s*$/g, // We have to close these tags to support XHTML (#13200) wrapMap = { // Support: IE9 option: [ 1, "" ], thead: [ 1, "", "
                                    " ], col: [ 2, "", "
                                    " ], tr: [ 2, "", "
                                    " ], td: [ 3, "", "
                                    " ], _default: [ 0, "", "" ] }; // Support: IE9 wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; // Support: 1.x compatibility // Manipulating tables requires a tbody function manipulationTarget( elem, content ) { return jQuery.nodeName( elem, "table" ) && jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? elem.getElementsByTagName("tbody")[0] || elem.appendChild( elem.ownerDocument.createElement("tbody") ) : elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { elem.type = match[ 1 ]; } else { elem.removeAttribute("type"); } return elem; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var i = 0, l = elems.length; for ( ; i < l; i++ ) { data_priv.set( elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) ); } } function cloneCopyEvent( src, dest ) { var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) { return; } // 1. Copy private data: events, handlers, etc. if ( data_priv.hasData( src ) ) { pdataOld = data_priv.access( src ); pdataCur = data_priv.set( dest, pdataOld ); events = pdataOld.events; if ( events ) { delete pdataCur.handle; pdataCur.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } } // 2. Copy user data if ( data_user.hasData( src ) ) { udataOld = data_user.access( src ); udataCur = jQuery.extend( {}, udataOld ); data_user.set( dest, udataCur ); } } function getAll( context, tag ) { var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : []; return tag === undefined || tag && jQuery.nodeName( context, tag ) ? jQuery.merge( [ context ], ret ) : ret; } // Fix IE bugs, see support tests function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. if ( nodeName === "input" && rcheckableType.test( src.type ) ) { dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, clone = elem.cloneNode( true ), inPage = jQuery.contains( elem.ownerDocument, elem ); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) { fixInput( srcElements[ i ], destElements[ i ] ); } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) { cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } // Return the cloned set return clone; }, buildFragment: function( elems, context, scripts, selection ) { var elem, tmp, tag, wrap, contains, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( jQuery.type( elem ) === "object" ) { // Support: QtWebKit, PhantomJS // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || fragment.appendChild( context.createElement("div") ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; // Descend through wrappers to the right content j = wrap[ 0 ]; while ( j-- ) { tmp = tmp.lastChild; } // Support: QtWebKit, PhantomJS // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container tmp = fragment.firstChild; // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; } } } // Remove wrapper from fragment fragment.textContent = ""; i = 0; while ( (elem = nodes[ i++ ]) ) { // #4087 - If origin and destination elements are the same, and this is // that element, do not do anything if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( (elem = tmp[ j++ ]) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } return fragment; }, cleanData: function( elems ) { var data, elem, type, key, special = jQuery.event.special, i = 0; for ( ; (elem = elems[ i ]) !== undefined; i++ ) { if ( jQuery.acceptData( elem ) ) { key = elem[ data_priv.expando ]; if ( key && (data = data_priv.cache[ key ]) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } if ( data_priv.cache[ key ] ) { // Discard any remaining `private` data delete data_priv.cache[ key ]; } } } // Discard any remaining `user` data delete data_user.cache[ elem[ data_user.expando ] ]; } } }); jQuery.fn.extend({ text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().each(function() { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.textContent = value; } }); }, null, value, arguments.length ); }, append: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } }); }, prepend: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } }); }, before: function() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } }); }, after: function() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } }); }, remove: function( selector, keepData /* Internal Use Only */ ) { var elem, elems = selector ? jQuery.filter( selector, this ) : this, i = 0; for ( ; (elem = elems[i]) != null; i++ ) { if ( !keepData && elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem ) ); } if ( elem.parentNode ) { if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { setGlobalEval( getAll( elem, "script" ) ); } elem.parentNode.removeChild( elem ); } } return this; }, empty: function() { var elem, i = 0; for ( ; (elem = this[i]) != null; i++ ) { if ( elem.nodeType === 1 ) { // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes elem.textContent = ""; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map(function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); }); }, html: function( value ) { return access( this, function( value ) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = value.replace( rxhtmlTag, "<$1>" ); try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function() { var arg = arguments[ 0 ]; // Make the changes, replacing each context element with the new content this.domManip( arguments, function( elem ) { arg = this.parentNode; jQuery.cleanData( getAll( this ) ); if ( arg ) { arg.replaceChild( elem, this ); } }); // Force removal if there was no new content (e.g., from empty arguments) return arg && (arg.length || arg.nodeType) ? this : this.remove(); }, detach: function( selector ) { return this.remove( selector, true ); }, domManip: function( args, callback ) { // Flatten any nested arrays args = concat.apply( [], args ); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = this.length, set = this, iNoClone = l - 1, value = args[ 0 ], isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( isFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test( value ) ) ) { return this.each(function( index ) { var self = set.eq( index ); if ( isFunction ) { args[ 0 ] = value.call( this, index, self.html() ); } self.domManip( args, callback ); }); } if ( l ) { fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } if ( first ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { // Support: QtWebKit // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( this[ i ], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ) { jQuery._evalUrl( node.src ); } } else { jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); } } } } } } return this; } }); jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, ret = [], insert = jQuery( selector ), last = insert.length - 1, i = 0; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems ); // Support: QtWebKit // .get() because push.apply(_, arraylike) throws push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; }); var iframe, elemdisplay = {}; /** * Retrieve the actual display of a element * @param {String} name nodeName of the element * @param {Object} doc Document object */ // Called only from within defaultDisplay function actualDisplay( name, doc ) { var style, elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), // getDefaultComputedStyle might be reliably used only on attached element display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? // Use of this method is a temporary fix (more like optimization) until something better comes along, // since it was removed from specification and supported only in FF style.display : jQuery.css( elem[ 0 ], "display" ); // We don't have any data stored on the element, // so use "detach" method as fast way to get rid of the element elem.detach(); return display; } /** * Try to determine the default display value of an element * @param {String} nodeName */ function defaultDisplay( nodeName ) { var doc = document, display = elemdisplay[ nodeName ]; if ( !display ) { display = actualDisplay( nodeName, doc ); // If the simple way fails, read from inside an iframe if ( display === "none" || !display ) { // Use the already-created iframe if possible iframe = (iframe || jQuery( "