diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0425900 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM golang as builder + +LABEL maintainer="Valentin KIVACHUK BURDA" + +ENV DOCKER_ENVIRONMENT=true +ENV CGO_ENABLED=0 +ENV GOOS=linux +ENV GO111MODULE=on + +EXPOSE 49618 + +WORKDIR /go/src/oc-search + +####################################################### +RUN go get github.com/beego/bee/v2 + +COPY go.mod . +COPY go.sum . +RUN go mod download -x + +# COPY . . +COPY main.go go.mod go.sum ./ + +COPY controllers controllers +COPY models models +COPY routers routers +COPY api-client api-client +COPY conf conf +COPY static static +COPY views views +COPY scripts scripts + +# FROM golang + +# WORKDIR /go/src/oc-catalog + +# COPY --from=builder /go/src/oc-catalog . + +ENV DOCKER_ENVIRONMENT=true + +RUN go build . + + +CMD [ "bee", "run" ] diff --git a/README.md b/README.md index 2c4c5d2..75c1b9f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# oc-search +# OC Search +Regenerate Swagger client with: + +`./scripts/generate_client.sh` \ No newline at end of file diff --git a/api-client/oc-catalog/.gitignore b/api-client/oc-catalog/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/api-client/oc-catalog/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/api-client/oc-catalog/.swagger-codegen-ignore b/api-client/oc-catalog/.swagger-codegen-ignore new file mode 100644 index 0000000..c5fa491 --- /dev/null +++ b/api-client/oc-catalog/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/api-client/oc-catalog/.swagger-codegen/VERSION b/api-client/oc-catalog/.swagger-codegen/VERSION new file mode 100644 index 0000000..6381ae0 --- /dev/null +++ b/api-client/oc-catalog/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.4.18 \ No newline at end of file diff --git a/api-client/oc-catalog/.travis.yml b/api-client/oc-catalog/.travis.yml new file mode 100644 index 0000000..f5cb2ce --- /dev/null +++ b/api-client/oc-catalog/.travis.yml @@ -0,0 +1,8 @@ +language: go + +install: + - go get -d -v . + +script: + - go build -v ./ + diff --git a/api-client/oc-catalog/README.md b/api-client/oc-catalog/README.md new file mode 100644 index 0000000..04c9497 --- /dev/null +++ b/api-client/oc-catalog/README.md @@ -0,0 +1,100 @@ +# Go API client for swagger + +Backend of the oc-search project + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: 1.0.0 +- Package version: 1.0.0 +- Build package: io.swagger.codegen.languages.GoClientCodegen + +## Installation +Put the package under your project folder and add the following in import: +```golang +import "./swagger" +``` + +## Documentation for API Endpoints + +All URIs are relative to *https://localhost/v1* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*ComputingApi* | [**ComputingControllerAddComputing**](docs/ComputingApi.md#computingcontrolleraddcomputing) | **Post** /computing/ | +*ComputingApi* | [**ComputingControllerGetComputingByID**](docs/ComputingApi.md#computingcontrollergetcomputingbyid) | **Get** /computing/{ID} | +*ComputingApi* | [**ComputingControllerGetMultipleComputingByIDs**](docs/ComputingApi.md#computingcontrollergetmultiplecomputingbyids) | **Get** /computing/multi/{IDs} | +*DataApi* | [**DataControllerCreateData**](docs/DataApi.md#datacontrollercreatedata) | **Post** /data/ | +*DataApi* | [**DataControllerGetDataByID**](docs/DataApi.md#datacontrollergetdatabyid) | **Get** /data/{ID} | +*DataApi* | [**DataControllerGetMultipleDataByIDs**](docs/DataApi.md#datacontrollergetmultipledatabyids) | **Get** /data/multi/{IDs} | +*DatacenterApi* | [**DatacenterControllerCreateDatacenter**](docs/DatacenterApi.md#datacentercontrollercreatedatacenter) | **Post** /datacenter/ | +*DatacenterApi* | [**DatacenterControllerGetMultipleDatacentersByIDs**](docs/DatacenterApi.md#datacentercontrollergetmultipledatacentersbyids) | **Get** /datacenter/multi/{IDs} | +*DatacenterApi* | [**DatacenterControllerGetOneDatacenter**](docs/DatacenterApi.md#datacentercontrollergetonedatacenter) | **Get** /datacenter/{ID} | +*ScheduleApi* | [**ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC**](docs/ScheduleApi.md#schedulecontrollercheckifschedulecanbecreatedinthisdc) | **Post** /schedule/check | +*ScheduleApi* | [**ScheduleControllerCreateSchedule**](docs/ScheduleApi.md#schedulecontrollercreateschedule) | **Post** /schedule/book | +*ScheduleApi* | [**ScheduleControllerGetNextSchedule**](docs/ScheduleApi.md#schedulecontrollergetnextschedule) | **Get** /schedule/next | +*ScheduleApi* | [**ScheduleControllerGetPreviousSchedule**](docs/ScheduleApi.md#schedulecontrollergetpreviousschedule) | **Get** /schedule/previous | +*ScheduleApi* | [**ScheduleControllerGetSchedules**](docs/ScheduleApi.md#schedulecontrollergetschedules) | **Get** /schedule/ | +*SearchApi* | [**SearchControllerSearchByWord**](docs/SearchApi.md#searchcontrollersearchbyword) | **Get** /search/byWord | +*StorageApi* | [**StorageControllerCreateStorage**](docs/StorageApi.md#storagecontrollercreatestorage) | **Post** /storage/ | +*StorageApi* | [**StorageControllerGet**](docs/StorageApi.md#storagecontrollerget) | **Get** /storage/{ID} | +*StorageApi* | [**StorageControllerGetMultipleStoragesByIDs**](docs/StorageApi.md#storagecontrollergetmultiplestoragesbyids) | **Get** /storage/multi/{IDs} | +*UserApi* | [**UserControllerLogin**](docs/UserApi.md#usercontrollerlogin) | **Get** /user/login | +*UserApi* | [**UserControllerLogout**](docs/UserApi.md#usercontrollerlogout) | **Get** /user/logout | +*WorkflowApi* | [**WorkflowControllerAddNewObjectToAWorkflow**](docs/WorkflowApi.md#workflowcontrolleraddnewobjecttoaworkflow) | **Post** /workflow/{workflowName}/add | +*WorkflowApi* | [**WorkflowControllerBookSchedule**](docs/WorkflowApi.md#workflowcontrollerbookschedule) | **Post** /workflow/{workflowName}/schedule/book | +*WorkflowApi* | [**WorkflowControllerCheckSchedule**](docs/WorkflowApi.md#workflowcontrollercheckschedule) | **Get** /workflow/{workflowName}/schedule/check | +*WorkflowApi* | [**WorkflowControllerCreateANewWorkflow**](docs/WorkflowApi.md#workflowcontrollercreateanewworkflow) | **Post** /workflow/ | +*WorkflowApi* | [**WorkflowControllerCreateARealtionshipBetweenTwoRobjects**](docs/WorkflowApi.md#workflowcontrollercreatearealtionshipbetweentworobjects) | **Post** /workflow/{workflowName}/link | +*WorkflowApi* | [**WorkflowControllerGetMxGraphLastStatus**](docs/WorkflowApi.md#workflowcontrollergetmxgraphlaststatus) | **Get** /workflow/{workflowName}/mxGraphParser | +*WorkflowApi* | [**WorkflowControllerGetSchedule**](docs/WorkflowApi.md#workflowcontrollergetschedule) | **Get** /workflow/{workflowName}/schedule | +*WorkflowApi* | [**WorkflowControllerGetWorkflow**](docs/WorkflowApi.md#workflowcontrollergetworkflow) | **Get** /workflow/{workflowName} | +*WorkflowApi* | [**WorkflowControllerListWorkflows**](docs/WorkflowApi.md#workflowcontrollerlistworkflows) | **Get** /workflow/ | +*WorkflowApi* | [**WorkflowControllerParseMxGraph**](docs/WorkflowApi.md#workflowcontrollerparsemxgraph) | **Post** /workflow/{workflowName}/mxGraphParser | +*WorkflowApi* | [**WorkflowControllerSetSchedule**](docs/WorkflowApi.md#workflowcontrollersetschedule) | **Put** /workflow/{workflowName}/schedule | +*WorkspaceApi* | [**WorkspaceControllerAddModelToWorkspace**](docs/WorkspaceApi.md#workspacecontrolleraddmodeltoworkspace) | **Post** /workspace/ | +*WorkspaceApi* | [**WorkspaceControllerDeleteElementFromUserWorkspace**](docs/WorkspaceApi.md#workspacecontrollerdeleteelementfromuserworkspace) | **Delete** /workspace/ | +*WorkspaceApi* | [**WorkspaceControllerGetFullWorkspace**](docs/WorkspaceApi.md#workspacecontrollergetfullworkspace) | **Get** /workspace/list_model | +*WorkspaceApi* | [**WorkspaceControllerGetWorkspace**](docs/WorkspaceApi.md#workspacecontrollergetworkspace) | **Get** /workspace/list | + + +## Documentation For Models + + - [ModelsComputingModel](docs/ModelsComputingModel.md) + - [ModelsComputingNewModel](docs/ModelsComputingNewModel.md) + - [ModelsComputingObject](docs/ModelsComputingObject.md) + - [ModelsDCstatus](docs/ModelsDCstatus.md) + - [ModelsDataModel](docs/ModelsDataModel.md) + - [ModelsDataNewModel](docs/ModelsDataNewModel.md) + - [ModelsDataObject](docs/ModelsDataObject.md) + - [ModelsDatacenterCpuModel](docs/ModelsDatacenterCpuModel.md) + - [ModelsDatacenterGpuModel](docs/ModelsDatacenterGpuModel.md) + - [ModelsDatacenterMemoryModel](docs/ModelsDatacenterMemoryModel.md) + - [ModelsDatacenterModel](docs/ModelsDatacenterModel.md) + - [ModelsDatacenterNewModel](docs/ModelsDatacenterNewModel.md) + - [ModelsDatacenterObject](docs/ModelsDatacenterObject.md) + - [ModelsExecutionRequirementsModel](docs/ModelsExecutionRequirementsModel.md) + - [ModelsRepositoryModel](docs/ModelsRepositoryModel.md) + - [ModelsScheduleDb](docs/ModelsScheduleDb.md) + - [ModelsScheduleInfo](docs/ModelsScheduleInfo.md) + - [ModelsScheduleTime](docs/ModelsScheduleTime.md) + - [ModelsSearchResult](docs/ModelsSearchResult.md) + - [ModelsStorageModel](docs/ModelsStorageModel.md) + - [ModelsStorageNewModel](docs/ModelsStorageNewModel.md) + - [ModelsStorageObject](docs/ModelsStorageObject.md) + - [ModelsWorkflow](docs/ModelsWorkflow.md) + - [ModelsWorkflowSchedule](docs/ModelsWorkflowSchedule.md) + - [ModelsWorkspace](docs/ModelsWorkspace.md) + - [ModelsWorkspaceModel](docs/ModelsWorkspaceModel.md) + - [PrimitiveObjectId](docs/PrimitiveObjectId.md) + - [TimeTime](docs/TimeTime.md) + + +## Documentation For Authorization + Endpoints do not require authorization. + + +## Author + +valentin.kivachuk@irt-saintexupery.com + diff --git a/api-client/oc-catalog/api/swagger.yaml b/api-client/oc-catalog/api/swagger.yaml new file mode 100644 index 0000000..d98f6df --- /dev/null +++ b/api-client/oc-catalog/api/swagger.yaml @@ -0,0 +1,2041 @@ +--- +swagger: "2.0" +info: + description: "Backend of the oc-search project" + version: "1.0.0" + title: "oc-catalog API" + contact: + email: "valentin.kivachuk@irt-saintexupery.com" +basePath: "/v1" +tags: +- name: "data" + description: "All operations related to the rType data\n" +- name: "computing" + description: "All operations related to the rType computing\n" +- name: "datacenter" + description: "DatacenterController operations about datacenters\n" +- name: "storage" + description: "StorageController operations about storage\n" +paths: + /computing/: + post: + tags: + - "computing" + description: "Submit a computing object" + operationId: "ComputingController.Add computing" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ComputingNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} ID" + "403": + description: "Missing body or fields" + /computing/multi/{IDs}: + get: + tags: + - "computing" + description: "Return Computing objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "ComputingController.Get multiple computing by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of computing IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /computing/{ID}: + get: + tags: + - "computing" + description: "Find a computing resource based on ID" + operationId: "ComputingController.Get computing by ID" + parameters: + - name: "ID" + in: "path" + description: "The ID of the resource" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "ID is empty" + /data/: + post: + tags: + - "data" + description: "Submit data object" + operationId: "DataController.Create Data" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.DataNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} ID" + "403": + description: "Missing body or fields" + /data/multi/{IDs}: + get: + tags: + - "data" + description: "Return Data object if found in the DB. Not found IDs will be ignored" + operationId: "DataController.Get multiple data by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of data IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + "403": + description: "IDs are empty" + /data/{ID}: + get: + tags: + - "data" + description: "Find rType data based on ID" + operationId: "DataController.Get data by ID" + parameters: + - name: "ID" + in: "path" + description: "The ID of the data resource" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.DataModel" + "403": + description: "ID is empty" + /datacenter/: + post: + tags: + - "datacenter" + description: "submit Datacenter object" + operationId: "DatacenterController.Create Datacenter" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.DatacenterNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} models.DatacenterModel" + "403": + description: "Missing body or fields" + /datacenter/multi/{IDs}: + get: + tags: + - "datacenter" + description: "Return Datacenter objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "DatacenterController.Get multiple datacenters by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of datacenter IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /datacenter/{ID}: + get: + tags: + - "datacenter" + description: "find datacenter by ID" + operationId: "DatacenterController.GetOneDatacenter" + parameters: + - name: "ID" + in: "path" + description: "the ID you want to get" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.DatacenterModel" + "403": + description: "ID is empty" + /schedule/: + get: + tags: + - "schedule" + description: "Get a list of next startDates schedules (inclusive). If timezone\ + \ is not specified, will assume UTC" + operationId: "ScheduleController.Get schedules" + parameters: + - name: "startDate" + in: "query" + description: "Start date" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "End date" + required: true + x-exportParamName: "StopDate" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ScheduleDB" + "201": + description: "Too much elements within the range of dates" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/book: + post: + tags: + - "schedule" + description: "Create schedule for a workflow. It will return some future executions\ + \ just as information" + operationId: "ScheduleController.Create schedule" + parameters: + - name: "dcName" + in: "query" + description: "Name of the node (oc-catalog) from where the workflow comes." + required: true + type: "string" + x-exportParamName: "DcName" + - name: "workflowName" + in: "query" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "cron" + in: "query" + description: "Cron syntax with year. If no year is specified, will use the\ + \ current" + required: true + type: "string" + x-exportParamName: "Cron" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: true + type: "integer" + format: "int32" + x-exportParamName: "Duration" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - in: "body" + name: "requirements" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ExecutionRequirementsModel" + x-exportParamName: "Requirements" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleInfo" + "400": + description: "workflowName not found or empty" + "403": + description: "Authentication issue" + /schedule/check: + post: + tags: + - "schedule" + description: "Check for availability of this DC" + operationId: "ScheduleController.Check if schedule can be created in this DC" + parameters: + - name: "cron" + in: "query" + description: "Cron syntax" + required: true + type: "string" + x-exportParamName: "Cron" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: true + type: "integer" + format: "int32" + x-exportParamName: "Duration" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - in: "body" + name: "requirements" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ExecutionRequirementsModel" + x-exportParamName: "Requirements" + responses: + "200": + description: "The schedule can be created" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/next: + get: + tags: + - "schedule" + description: "Give a date, get the next date where there are at least on schedule.\ + \ If no hours specified, will assume 00:00" + operationId: "ScheduleController.Get next schedule" + parameters: + - name: "baseDate" + in: "query" + description: "Base date" + required: true + x-exportParamName: "BaseDate" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/*time.Time" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/previous: + get: + tags: + - "schedule" + description: "Give a date, get the previous date where there are at least on\ + \ schedule. If no hours specified, will assume 00:00" + operationId: "ScheduleController.Get previous schedule" + parameters: + - name: "baseDate" + in: "query" + description: "Base date" + required: true + x-exportParamName: "BaseDate" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/*time.Time" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /search/byWord: + get: + tags: + - "search" + description: "find resources by word" + operationId: "SearchController.Search by word" + parameters: + - name: "word" + in: "query" + description: "Word to search across all resources" + required: true + type: "string" + x-exportParamName: "Word" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.SearchResult" + "503": + description: "Internal error" + /storage/: + post: + tags: + - "storage" + description: "submit storage object" + operationId: "StorageController.Create Storage" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.StorageNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} models.StorageModel" + "403": + description: "Missing body or fields" + /storage/multi/{IDs}: + get: + tags: + - "storage" + description: "Return Storage objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "StorageController.Get multiple storages by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of storage IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /storage/{ID}: + get: + tags: + - "storage" + description: "find storage by ID" + operationId: "StorageController.Get" + parameters: + - name: "ID" + in: "path" + description: "the ID you want to get" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.StorageModel" + "403": + description: "ID is empty" + /user/login: + get: + tags: + - "user" + description: "Logs user into the system" + operationId: "UserController.Login" + parameters: + - name: "username" + in: "query" + description: "The username for login" + required: true + type: "string" + x-exportParamName: "Username" + - name: "password" + in: "query" + description: "The password for login" + required: true + type: "string" + x-exportParamName: "Password" + responses: + "200": + description: "{string} login success" + "403": + description: "user not exist" + /user/logout: + get: + tags: + - "user" + description: "Logs out current logged in user session" + operationId: "UserController.logout" + parameters: [] + responses: + "200": + description: "{string} logout success" + /workflow/: + get: + tags: + - "workflow" + description: "List available workflows" + operationId: "WorkflowController.List workflows" + parameters: [] + responses: + "200": + description: "[]string List of workflows" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + post: + tags: + - "workflow" + description: "Create a name for the new workflow" + operationId: "WorkflowController.Create a new workflow" + parameters: + - name: "workflowName" + in: "query" + description: "Name of the workflow" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "{string} Workflow created succeful" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}: + get: + tags: + - "workflow" + description: "Get a workflow by name" + operationId: "WorkflowController.Get Workflow" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.Workflow" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/add: + post: + tags: + - "workflow" + description: "Create a Rtype object from already added resources to the workspace" + operationId: "WorkflowController.Add new object to a Workflow" + parameters: + - name: "workflowName" + in: "path" + description: "workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "rID" + in: "query" + description: "rID of already existing item in Workspace" + required: true + type: "string" + x-exportParamName: "RID" + responses: + "200": + description: "{string} ID of the new object (rObjID)" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/link: + post: + tags: + - "workflow" + description: "Create a Rtype object from already added resources to the workspace" + operationId: "WorkflowController.Create a realtionship between two Robjects" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "rObjIDsource" + in: "query" + description: "Robject source. Usually Data" + required: true + type: "string" + x-exportParamName: "RObjIDsource" + - name: "isInput" + in: "query" + description: "If the operation is for input (true) linkage or output (false)" + required: true + type: "boolean" + x-exportParamName: "IsInput" + - name: "rObjIDtarger" + in: "query" + description: "Robject where will be written the association" + required: true + type: "string" + x-exportParamName: "RObjIDtarger" + responses: + "200": + description: "{string} ID of the new object (rObjID)" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/mxGraphParser: + get: + tags: + - "workflow" + description: "Obtain the last mxgraph XML status from the workflow" + operationId: "WorkflowController.Get mxGraph last status" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "The xmlgraph" + "201": + description: "Empty workflow" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + post: + tags: + - "workflow" + description: "If we use this aproach to transofrm mxgraph representation in\ + \ our representation, we should not use other API calls for modify the project\ + \ structure or we'll have inconsistencies." + operationId: "WorkflowController.Parse mxGraph" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - in: "body" + name: "xmlData" + description: "Xml representation of the workflow" + required: true + schema: + type: "string" + x-exportParamName: "XmlData" + responses: + "200": + description: "The xmlgraph consumed correctly" + "201": + description: "The xmlgraph consumed with issues" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule: + get: + tags: + - "workflow" + description: "Obtain the desired schedule of this workflow" + operationId: "WorkflowController.Get Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleTime" + "400": + description: "Workflow doesn't exist" + "401": + description: "Other error" + "403": + description: "Authentication issue" + put: + tags: + - "workflow" + description: "Set desired schedule by the user. No other effects a part of saving\ + \ the user input" + operationId: "WorkflowController.Set Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "isService" + in: "query" + description: "True: Service, False: Task" + required: true + type: "boolean" + x-exportParamName: "IsService" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - name: "events" + in: "query" + description: "List of events separated by comma" + required: false + type: "string" + x-exportParamName: "Events" + x-optionalDataType: "String" + - name: "cronString" + in: "query" + description: "Cron string" + required: false + type: "string" + x-exportParamName: "CronString" + x-optionalDataType: "String" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: false + type: "integer" + format: "int32" + x-exportParamName: "Duration" + x-optionalDataType: "Int32" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleInfo" + "400": + description: "Workflow doesn't exist" + "401": + description: "Other error" + "402": + description: "Bad user input" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule/book: + post: + tags: + - "workflow" + description: "Book a schedule in all DCs of the workflow. Must set a desired\ + \ schedule first!" + operationId: "WorkflowController.Book Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DCstatus" + "401": + description: "Other error. Check output" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule/check: + get: + tags: + - "workflow" + description: "Check if we can schedule the project in other DCs. Must set a\ + \ desired schedule first!" + operationId: "WorkflowController.Check Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DCstatus" + "401": + description: "Other error" + "403": + description: "Authentication issue" + /workspace/: + post: + tags: + - "workspace" + description: "Insert a resource in the workspace" + operationId: "WorkspaceController.Add model to workspace" + parameters: + - name: "id" + in: "query" + description: "ID of a resource" + required: true + type: "string" + x-exportParamName: "Id" + - name: "rtype" + in: "query" + description: "Type of resource" + required: true + type: "string" + x-exportParamName: "Rtype" + responses: + "200": + description: "{string} login success" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + delete: + tags: + - "workspace" + description: "Remove a resource from the workspace" + operationId: "WorkspaceController.Delete element from user workspace" + parameters: + - name: "id" + in: "query" + description: "ID of a resource" + required: true + type: "string" + x-exportParamName: "Id" + - name: "rtype" + in: "query" + description: "Type of resource" + required: true + type: "string" + x-exportParamName: "Rtype" + responses: + "200": + description: "{string} Removed succeful" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workspace/list: + get: + tags: + - "workspace" + description: "Get workspace elements based on user_id token" + operationId: "WorkspaceController.Get workspace" + parameters: [] + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.Workspace" + "403": + description: "Authentication issue" + /workspace/list_model: + get: + tags: + - "workspace" + description: "Get full workspace elements based on user_id token" + operationId: "WorkspaceController.Get full workspace" + parameters: [] + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.WorkspaceModel" + "403": + description: "Authentication issue" +definitions: + '*time.Time': + type: "object" + title: "Time" + models.ComputingModel: + type: "object" + required: + - "ID" + properties: + Dinputs: + type: "array" + items: + type: "string" + Doutputs: + type: "array" + items: + type: "string" + ID: + type: "string" + example: "5099803df3f4948bd2f98391" + description: + type: "string" + execution_requirements: + $ref: "#/definitions/models.ExecutionRequirementsModel" + license: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the computing" + owner: + type: "string" + price: + type: "integer" + format: "int32" + repository: + $ref: "#/definitions/models.RepositoryModel" + short_description: + type: "string" + type: + type: "string" + title: "ComputingModel" + example: + owner: "owner" + short_description: "short_description" + description: "description" + repository: + credentials: "credentials" + url: "url" + type: "type" + Doutputs: + - "Doutputs" + - "Doutputs" + license: "license" + price: 5 + name: "name" + logo: "logo" + Dinputs: + - "Dinputs" + - "Dinputs" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + models.ComputingNEWModel: + type: "object" + required: + - "description" + - "logo" + - "name" + - "short_description" + - "type" + properties: + Dinputs: + type: "array" + items: + type: "string" + Doutputs: + type: "array" + items: + type: "string" + description: + type: "string" + execution_requirements: + $ref: "#/definitions/models.ExecutionRequirementsModel" + license: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the computing" + owner: + type: "string" + price: + type: "integer" + format: "int32" + repository: + $ref: "#/definitions/models.RepositoryModel" + short_description: + type: "string" + type: + type: "string" + title: "ComputingNEWModel" + models.ComputingObject: + type: "object" + properties: + datacenterID: + type: "string" + description: "Datacenter where the computing will be executed" + inputs: + type: "array" + items: + type: "string" + outputs: + type: "array" + items: + type: "string" + referenceID: + description: "Computing model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "ComputingObject" + example: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + models.DCstatus: + type: "object" + properties: + Booked: + $ref: "#/definitions/models.ScheduleInfo" + DCname: + type: "string" + DCobjID: + type: "string" + ErrorMessage: + type: "string" + IsAvailable: + type: "boolean" + IsReachable: + type: "boolean" + title: "DCstatus" + example: + DCobjID: "DCobjID" + DCname: "DCname" + IsAvailable: true + Booked: + Total: 0 + NextExecutions: + - "NextExecutions" + - "NextExecutions" + IsReachable: true + ErrorMessage: "ErrorMessage" + models.DataModel: + type: "object" + required: + - "ID" + properties: + ID: + type: "string" + description: + type: "string" + dtype: + type: "string" + example: + type: "string" + description: "base64 encoded data" + location: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the data" + protocol: + type: "array" + items: + type: "string" + short_description: + type: "string" + type: + type: "string" + example: "file" + description: "Define type of data" + title: "DataModel" + example: + short_description: "short_description" + protocol: + - "protocol" + - "protocol" + name: "name" + description: "description" + dtype: "dtype" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + models.DataNEWModel: + type: "object" + required: + - "description" + - "example" + - "location" + - "logo" + - "name" + - "short_description" + - "type" + properties: + description: + type: "string" + dtype: + type: "string" + example: + type: "string" + description: "base64 encoded data" + location: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the data" + protocol: + type: "array" + items: + type: "string" + short_description: + type: "string" + type: + type: "string" + example: "file" + description: "Define type of data" + title: "DataNEWModel" + models.DataObject: + type: "object" + properties: + referenceID: + description: "Data model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "DataObject" + example: {} + models.DatacenterCpuModel: + type: "object" + required: + - "cores" + properties: + architecture: + type: "string" + cores: + type: "integer" + format: "int32" + minimum_memory: + type: "integer" + format: "int32" + platform: + type: "string" + shared: + type: "boolean" + title: "DatacenterCpuModel" + example: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + models.DatacenterGpuModel: + type: "object" + properties: + cuda_cores: + type: "integer" + format: "int32" + memory: + type: "integer" + format: "int32" + description: "Units in MB" + model: + type: "string" + tensor_cores: + type: "integer" + format: "int32" + title: "DatacenterGpuModel" + example: + memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + models.DatacenterMemoryModel: + type: "object" + properties: + ecc: + type: "boolean" + size: + type: "integer" + format: "int32" + description: "Units in MB" + title: "DatacenterMemoryModel" + example: + ecc: true + size: 7 + models.DatacenterModel: + type: "object" + required: + - "ID" + properties: + ID: + type: "string" + acronym: + type: "string" + description: "id of the DC" + bookingPrice: + type: "integer" + format: "int64" + cpu: + $ref: "#/definitions/models.DatacenterCpuModel" + description: + type: "string" + gpu: + type: "array" + items: + $ref: "#/definitions/models.DatacenterGpuModel" + hosts: + type: "array" + description: "list of host:port" + items: + type: "string" + logo: + type: "string" + name: + type: "string" + owner: + type: "string" + ram: + $ref: "#/definitions/models.DatacenterMemoryModel" + short_description: + type: "string" + type: + type: "string" + title: "DatacenterModel" + example: + owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + models.DatacenterNEWModel: + type: "object" + required: + - "acronym" + - "cpu" + - "description" + - "gpu" + - "hosts" + - "logo" + - "name" + - "ram" + - "short_description" + - "type" + properties: + acronym: + type: "string" + description: "id of the DC" + bookingPrice: + type: "integer" + format: "int64" + cpu: + $ref: "#/definitions/models.DatacenterCpuModel" + description: + type: "string" + gpu: + type: "array" + items: + $ref: "#/definitions/models.DatacenterGpuModel" + hosts: + type: "array" + description: "list of host:port" + items: + type: "string" + logo: + type: "string" + name: + type: "string" + owner: + type: "string" + ram: + $ref: "#/definitions/models.DatacenterMemoryModel" + short_description: + type: "string" + type: + type: "string" + title: "DatacenterNEWModel" + models.DatacenterObject: + type: "object" + properties: + referenceID: + description: "Data model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "DatacenterObject" + example: {} + models.ExecutionRequirementsModel: + type: "object" + required: + - "cpus" + - "ram" + properties: + cpus: + type: "integer" + format: "int32" + disk_io: + type: "string" + gpus: + type: "integer" + format: "int32" + description: "Amount of GPUs needed" + parallel: + type: "boolean" + ram: + type: "integer" + format: "int32" + description: "Units in MB" + scaling_model: + type: "integer" + format: "int32" + title: "ExecutionRequirementsModel" + example: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + models.RepositoryModel: + type: "object" + properties: + credentials: + type: "string" + url: + type: "string" + title: "RepositoryModel" + example: + credentials: "credentials" + url: "url" + models.ScheduleDB: + type: "object" + properties: + ResourceQty: + $ref: "#/definitions/models.ExecutionRequirementsModel" + StartDate: + type: "string" + format: "datetime" + StopDate: + type: "string" + format: "datetime" + Workflow: + type: "string" + title: "ScheduleDB" + example: + StartDate: "StartDate" + ResourceQty: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + StopDate: "StopDate" + Workflow: "Workflow" + models.ScheduleInfo: + type: "object" + properties: + NextExecutions: + type: "array" + items: + type: "string" + Total: + type: "integer" + format: "int64" + title: "ScheduleInfo" + example: + Total: 0 + NextExecutions: + - "NextExecutions" + - "NextExecutions" + models.ScheduleTime: + type: "object" + title: "ScheduleTime" + models.SearchResult: + type: "object" + required: + - "computing" + properties: + computing: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + data: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + datacenter: + type: "array" + items: + $ref: "#/definitions/models.DatacenterModel" + storage: + type: "array" + items: + $ref: "#/definitions/models.StorageModel" + title: "SearchResult" + example: + computing: + - owner: "owner" + short_description: "short_description" + description: "description" + repository: + credentials: "credentials" + url: "url" + type: "type" + Doutputs: + - "Doutputs" + - "Doutputs" + license: "license" + price: 5 + name: "name" + logo: "logo" + Dinputs: + - "Dinputs" + - "Dinputs" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + - owner: "owner" + short_description: "short_description" + description: "description" + repository: + credentials: "credentials" + url: "url" + type: "type" + Doutputs: + - "Doutputs" + - "Doutputs" + license: "license" + price: 5 + name: "name" + logo: "logo" + Dinputs: + - "Dinputs" + - "Dinputs" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + data: + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + name: "name" + description: "description" + dtype: "dtype" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + name: "name" + description: "description" + dtype: "dtype" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + datacenter: + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + storage: + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + models.StorageModel: + type: "object" + required: + - "ID" + properties: + DCacronym: + type: "string" + description: "Unique ID of the DC where it is the storage" + ID: + type: "string" + bookingPrice: + type: "integer" + format: "int32" + description: + type: "string" + encryption: + type: "boolean" + logo: + type: "string" + name: + type: "string" + redundancy: + type: "string" + short_description: + type: "string" + size: + type: "integer" + format: "int32" + throughput: + type: "string" + type: + type: "string" + title: "StorageModel" + example: + short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + models.StorageNEWModel: + type: "object" + required: + - "DCacronym" + - "description" + - "logo" + - "name" + - "short_description" + - "size" + - "type" + properties: + DCacronym: + type: "string" + description: "Unique ID of the DC where it is the storage" + bookingPrice: + type: "integer" + format: "int32" + description: + type: "string" + encryption: + type: "boolean" + logo: + type: "string" + name: + type: "string" + redundancy: + type: "string" + short_description: + type: "string" + size: + type: "integer" + format: "int32" + throughput: + type: "string" + type: + type: "string" + title: "StorageNEWModel" + models.StorageObject: + type: "object" + properties: + inputs: + type: "array" + items: + type: "string" + outputs: + type: "array" + items: + type: "string" + referenceID: + description: "Storage model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "StorageObject" + example: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + models.Workflow: + type: "object" + properties: + MxgraphXML: + type: "string" + description: "State of the mxgraph" + computing: + $ref: "#/definitions/models.ComputingObject" + data: + $ref: "#/definitions/models.DataObject" + datacenter: + $ref: "#/definitions/models.DatacenterObject" + schedules: + $ref: "#/definitions/models.WorkflowSchedule" + storage: + $ref: "#/definitions/models.StorageObject" + title: "Workflow" + example: + computing: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + data: {} + schedules: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + datacenter: {} + storage: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + MxgraphXML: "MxgraphXML" + models.WorkflowSchedule: + type: "object" + properties: + IsService: + type: "boolean" + description: "Service: true, Task: false" + StartDate: + type: "string" + format: "datetime" + StopDate: + type: "string" + format: "datetime" + cron: + type: "string" + duration: + type: "integer" + format: "int32" + example: 7200 + description: "Durantion in seconds" + events: + type: "string" + isBooked: + type: "boolean" + title: "WorkflowSchedule" + example: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + models.Workspace: + type: "object" + properties: + Workflows: + $ref: "#/definitions/models.Workflow" + computing: + type: "array" + items: + type: "string" + data: + type: "array" + items: + type: "string" + datacenter: + type: "array" + items: + type: "string" + storage: + type: "array" + items: + type: "string" + user_id: + type: "string" + title: "Workspace" + example: + computing: + - "computing" + - "computing" + data: + - "data" + - "data" + Workflows: + computing: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + data: {} + schedules: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + datacenter: {} + storage: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + MxgraphXML: "MxgraphXML" + user_id: "user_id" + datacenter: + - "datacenter" + - "datacenter" + storage: + - "storage" + - "storage" + models.WorkspaceModel: + type: "object" + properties: + computing: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + data: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + datacenter: + type: "array" + items: + $ref: "#/definitions/models.DatacenterModel" + storage: + type: "array" + items: + $ref: "#/definitions/models.StorageModel" + user_id: + type: "string" + title: "WorkspaceModel" + example: + computing: + - owner: "owner" + short_description: "short_description" + description: "description" + repository: + credentials: "credentials" + url: "url" + type: "type" + Doutputs: + - "Doutputs" + - "Doutputs" + license: "license" + price: 5 + name: "name" + logo: "logo" + Dinputs: + - "Dinputs" + - "Dinputs" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + - owner: "owner" + short_description: "short_description" + description: "description" + repository: + credentials: "credentials" + url: "url" + type: "type" + Doutputs: + - "Doutputs" + - "Doutputs" + license: "license" + price: 5 + name: "name" + logo: "logo" + Dinputs: + - "Dinputs" + - "Dinputs" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + data: + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + name: "name" + description: "description" + dtype: "dtype" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + name: "name" + description: "description" + dtype: "dtype" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + user_id: "user_id" + datacenter: + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + storage: + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + primitive.ObjectID: + type: "object" + title: "ObjectID" + time.Time: + type: "object" + title: "Time" diff --git a/api-client/oc-catalog/api_computing.go b/api-client/oc-catalog/api_computing.go new file mode 100644 index 0000000..1d0bab0 --- /dev/null +++ b/api-client/oc-catalog/api_computing.go @@ -0,0 +1,275 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" +) + +// Linger please +var ( + _ context.Context +) + +type ComputingApiService service + +/* +ComputingApiService +Submit a computing object + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param body The object content + + +*/ +func (a *ComputingApiService) ComputingControllerAddComputing(ctx context.Context, body ModelsComputingNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +ComputingApiService +Find a computing resource based on ID + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iD The ID of the resource + +@return ModelsComputingModel +*/ +func (a *ComputingApiService) ComputingControllerGetComputingByID(ctx context.Context, iD string) (ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ComputingApiService +Return Computing objects if found in the DB. Not found IDs will be ignored + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iDs List of computing IDs + +@return []ModelsComputingModel +*/ +func (a *ComputingApiService) ComputingControllerGetMultipleComputingByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_data.go b/api-client/oc-catalog/api_data.go new file mode 100644 index 0000000..9b1f176 --- /dev/null +++ b/api-client/oc-catalog/api_data.go @@ -0,0 +1,275 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" +) + +// Linger please +var ( + _ context.Context +) + +type DataApiService service + +/* +DataApiService +Submit data object + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param body The object content + + +*/ +func (a *DataApiService) DataControllerCreateData(ctx context.Context, body ModelsDataNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +DataApiService +Find rType data based on ID + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iD The ID of the data resource + +@return ModelsDataModel +*/ +func (a *DataApiService) DataControllerGetDataByID(ctx context.Context, iD string) (ModelsDataModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsDataModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsDataModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +DataApiService +Return Data object if found in the DB. Not found IDs will be ignored + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iDs List of data IDs + +@return []ModelsDataModel +*/ +func (a *DataApiService) DataControllerGetMultipleDataByIDs(ctx context.Context, iDs []string) ([]ModelsDataModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDataModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDataModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_datacenter.go b/api-client/oc-catalog/api_datacenter.go new file mode 100644 index 0000000..6242588 --- /dev/null +++ b/api-client/oc-catalog/api_datacenter.go @@ -0,0 +1,275 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" +) + +// Linger please +var ( + _ context.Context +) + +type DatacenterApiService service + +/* +DatacenterApiService +submit Datacenter object + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param body The object content + + +*/ +func (a *DatacenterApiService) DatacenterControllerCreateDatacenter(ctx context.Context, body ModelsDatacenterNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +DatacenterApiService +Return Datacenter objects if found in the DB. Not found IDs will be ignored + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iDs List of datacenter IDs + +@return []ModelsComputingModel +*/ +func (a *DatacenterApiService) DatacenterControllerGetMultipleDatacentersByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +DatacenterApiService +find datacenter by ID + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iD the ID you want to get + +@return ModelsDatacenterModel +*/ +func (a *DatacenterApiService) DatacenterControllerGetOneDatacenter(ctx context.Context, iD string) (ModelsDatacenterModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsDatacenterModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsDatacenterModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_schedule.go b/api-client/oc-catalog/api_schedule.go new file mode 100644 index 0000000..d639d90 --- /dev/null +++ b/api-client/oc-catalog/api_schedule.go @@ -0,0 +1,471 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type ScheduleApiService service + +/* +ScheduleApiService +Check for availability of this DC + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param cron Cron syntax + * @param duration Duration in seconds + * @param startDate RFC3339 time for startDate + * @param stopDate RFC3339 time for stopDate + * @param requirements The object content + + +*/ +func (a *ScheduleApiService) ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC(ctx context.Context, cron string, duration int32, startDate interface{}, stopDate interface{}, requirements ModelsExecutionRequirementsModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/check" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("cron", parameterToString(cron, "")) + localVarQueryParams.Add("duration", parameterToString(duration, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &requirements + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +ScheduleApiService +Create schedule for a workflow. It will return some future executions just as information + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param dcName Name of the node (oc-catalog) from where the workflow comes. + * @param workflowName Workflow Name + * @param cron Cron syntax with year. If no year is specified, will use the current + * @param duration Duration in seconds + * @param startDate RFC3339 time for startDate + * @param stopDate RFC3339 time for stopDate + * @param requirements The object content + +@return ModelsScheduleInfo +*/ +func (a *ScheduleApiService) ScheduleControllerCreateSchedule(ctx context.Context, dcName string, workflowName string, cron string, duration int32, startDate interface{}, stopDate interface{}, requirements ModelsExecutionRequirementsModel) (ModelsScheduleInfo, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleInfo + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/book" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("dcName", parameterToString(dcName, "")) + localVarQueryParams.Add("workflowName", parameterToString(workflowName, "")) + localVarQueryParams.Add("cron", parameterToString(cron, "")) + localVarQueryParams.Add("duration", parameterToString(duration, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &requirements + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleInfo + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Give a date, get the next date where there are at least on schedule. If no hours specified, will assume 00:00 + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param baseDate Base date + +@return TimeTime +*/ +func (a *ScheduleApiService) ScheduleControllerGetNextSchedule(ctx context.Context, baseDate interface{}) (TimeTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue TimeTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/next" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("baseDate", parameterToString(baseDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v TimeTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Give a date, get the previous date where there are at least on schedule. If no hours specified, will assume 00:00 + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param baseDate Base date + +@return TimeTime +*/ +func (a *ScheduleApiService) ScheduleControllerGetPreviousSchedule(ctx context.Context, baseDate interface{}) (TimeTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue TimeTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/previous" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("baseDate", parameterToString(baseDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v TimeTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Get a list of next startDates schedules (inclusive). If timezone is not specified, will assume UTC + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param startDate Start date + * @param stopDate End date + +@return []ModelsScheduleDb +*/ +func (a *ScheduleApiService) ScheduleControllerGetSchedules(ctx context.Context, startDate interface{}, stopDate interface{}) ([]ModelsScheduleDb, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsScheduleDb + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsScheduleDb + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_search.go b/api-client/oc-catalog/api_search.go new file mode 100644 index 0000000..cf74fa2 --- /dev/null +++ b/api-client/oc-catalog/api_search.go @@ -0,0 +1,115 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type SearchApiService service + +/* +SearchApiService +find resources by word + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param word Word to search across all resources + +@return ModelsSearchResult +*/ +func (a *SearchApiService) SearchControllerSearchByWord(ctx context.Context, word string) (ModelsSearchResult, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsSearchResult + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/search/byWord" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("word", parameterToString(word, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsSearchResult + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_storage.go b/api-client/oc-catalog/api_storage.go new file mode 100644 index 0000000..a34b776 --- /dev/null +++ b/api-client/oc-catalog/api_storage.go @@ -0,0 +1,275 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" +) + +// Linger please +var ( + _ context.Context +) + +type StorageApiService service + +/* +StorageApiService +submit storage object + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param body The object content + + +*/ +func (a *StorageApiService) StorageControllerCreateStorage(ctx context.Context, body ModelsStorageNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +StorageApiService +find storage by ID + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iD the ID you want to get + +@return ModelsStorageModel +*/ +func (a *StorageApiService) StorageControllerGet(ctx context.Context, iD string) (ModelsStorageModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsStorageModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsStorageModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +StorageApiService +Return Storage objects if found in the DB. Not found IDs will be ignored + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param iDs List of storage IDs + +@return []ModelsComputingModel +*/ +func (a *StorageApiService) StorageControllerGetMultipleStoragesByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_user.go b/api-client/oc-catalog/api_user.go new file mode 100644 index 0000000..10e1f52 --- /dev/null +++ b/api-client/oc-catalog/api_user.go @@ -0,0 +1,170 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type UserApiService service + +/* +UserApiService +Logs user into the system + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param username The username for login + * @param password The password for login + + +*/ +func (a *UserApiService) UserControllerLogin(ctx context.Context, username string, password string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/user/login" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("username", parameterToString(username, "")) + localVarQueryParams.Add("password", parameterToString(password, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +UserApiService +Logs out current logged in user session + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + + +*/ +func (a *UserApiService) UserControllerLogout(ctx context.Context) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/user/logout" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_workflow.go b/api-client/oc-catalog/api_workflow.go new file mode 100644 index 0000000..cbbe997 --- /dev/null +++ b/api-client/oc-catalog/api_workflow.go @@ -0,0 +1,926 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" + "github.com/antihax/optional" +) + +// Linger please +var ( + _ context.Context +) + +type WorkflowApiService service + +/* +WorkflowApiService +Create a Rtype object from already added resources to the workspace + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName workflow Name + * @param rID rID of already existing item in Workspace + + +*/ +func (a *WorkflowApiService) WorkflowControllerAddNewObjectToAWorkflow(ctx context.Context, workflowName string, rID string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/add" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("rID", parameterToString(rID, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Book a schedule in all DCs of the workflow. Must set a desired schedule first! + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + +@return []ModelsDCstatus +*/ +func (a *WorkflowApiService) WorkflowControllerBookSchedule(ctx context.Context, workflowName string) ([]ModelsDCstatus, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDCstatus + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule/book" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDCstatus + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Check if we can schedule the project in other DCs. Must set a desired schedule first! + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + +@return []ModelsDCstatus +*/ +func (a *WorkflowApiService) WorkflowControllerCheckSchedule(ctx context.Context, workflowName string) ([]ModelsDCstatus, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDCstatus + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule/check" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDCstatus + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Create a name for the new workflow + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Name of the workflow + + +*/ +func (a *WorkflowApiService) WorkflowControllerCreateANewWorkflow(ctx context.Context, workflowName string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("workflowName", parameterToString(workflowName, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Create a Rtype object from already added resources to the workspace + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + * @param rObjIDsource Robject source. Usually Data + * @param isInput If the operation is for input (true) linkage or output (false) + * @param rObjIDtarger Robject where will be written the association + + +*/ +func (a *WorkflowApiService) WorkflowControllerCreateARealtionshipBetweenTwoRobjects(ctx context.Context, workflowName string, rObjIDsource string, isInput bool, rObjIDtarger string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/link" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("rObjIDsource", parameterToString(rObjIDsource, "")) + localVarQueryParams.Add("isInput", parameterToString(isInput, "")) + localVarQueryParams.Add("rObjIDtarger", parameterToString(rObjIDtarger, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Obtain the last mxgraph XML status from the workflow + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + + +*/ +func (a *WorkflowApiService) WorkflowControllerGetMxGraphLastStatus(ctx context.Context, workflowName string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/mxGraphParser" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Obtain the desired schedule of this workflow + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + +@return ModelsScheduleTime +*/ +func (a *WorkflowApiService) WorkflowControllerGetSchedule(ctx context.Context, workflowName string) (ModelsScheduleTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Get a workflow by name + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + +@return ModelsWorkflow +*/ +func (a *WorkflowApiService) WorkflowControllerGetWorkflow(ctx context.Context, workflowName string) (ModelsWorkflow, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkflow + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkflow + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +List available workflows + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + + +*/ +func (a *WorkflowApiService) WorkflowControllerListWorkflows(ctx context.Context) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +If we use this aproach to transofrm mxgraph representation in our representation, we should not use other API calls for modify the project structure or we'll have inconsistencies. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + * @param xmlData Xml representation of the workflow + + +*/ +func (a *WorkflowApiService) WorkflowControllerParseMxGraph(ctx context.Context, workflowName string, xmlData string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/mxGraphParser" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &xmlData + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Set desired schedule by the user. No other effects a part of saving the user input + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + * @param isService True: Service, False: Task + * @param startDate RFC3339 time for startDate + * @param stopDate RFC3339 time for stopDate + * @param optional nil or *WorkflowApiWorkflowControllerSetScheduleOpts - Optional Parameters: + * @param "Events" (optional.String) - List of events separated by comma + * @param "CronString" (optional.String) - Cron string + * @param "Duration" (optional.Int32) - Duration in seconds + +@return ModelsScheduleInfo +*/ + +type WorkflowApiWorkflowControllerSetScheduleOpts struct { + Events optional.String + CronString optional.String + Duration optional.Int32 +} + +func (a *WorkflowApiService) WorkflowControllerSetSchedule(ctx context.Context, workflowName string, isService bool, startDate interface{}, stopDate interface{}, localVarOptionals *WorkflowApiWorkflowControllerSetScheduleOpts) (ModelsScheduleInfo, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Put") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleInfo + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("isService", parameterToString(isService, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + if localVarOptionals != nil && localVarOptionals.Events.IsSet() { + localVarQueryParams.Add("events", parameterToString(localVarOptionals.Events.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.CronString.IsSet() { + localVarQueryParams.Add("cronString", parameterToString(localVarOptionals.CronString.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Duration.IsSet() { + localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleInfo + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/api_workspace.go b/api-client/oc-catalog/api_workspace.go new file mode 100644 index 0000000..2628331 --- /dev/null +++ b/api-client/oc-catalog/api_workspace.go @@ -0,0 +1,344 @@ + +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type WorkspaceApiService service + +/* +WorkspaceApiService +Insert a resource in the workspace + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id ID of a resource + * @param rtype Type of resource + + +*/ +func (a *WorkspaceApiService) WorkspaceControllerAddModelToWorkspace(ctx context.Context, id string, rtype string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("id", parameterToString(id, "")) + localVarQueryParams.Add("rtype", parameterToString(rtype, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Remove a resource from the workspace + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id ID of a resource + * @param rtype Type of resource + + +*/ +func (a *WorkspaceApiService) WorkspaceControllerDeleteElementFromUserWorkspace(ctx context.Context, id string, rtype string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Delete") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("id", parameterToString(id, "")) + localVarQueryParams.Add("rtype", parameterToString(rtype, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Get full workspace elements based on user_id token + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + +@return ModelsWorkspaceModel +*/ +func (a *WorkspaceApiService) WorkspaceControllerGetFullWorkspace(ctx context.Context) (ModelsWorkspaceModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkspaceModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/list_model" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkspaceModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Get workspace elements based on user_id token + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + +@return ModelsWorkspace +*/ +func (a *WorkspaceApiService) WorkspaceControllerGetWorkspace(ctx context.Context) (ModelsWorkspace, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkspace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/list" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkspace + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + diff --git a/api-client/oc-catalog/client.go b/api-client/oc-catalog/client.go new file mode 100644 index 0000000..ff916aa --- /dev/null +++ b/api-client/oc-catalog/client.go @@ -0,0 +1,499 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:(?:application|text)/json)") + xmlCheck = regexp.MustCompile("(?i:(?:application|text)/xml)") +) + +// APIClient manages communication with the oc-catalog API API v1.0.0 +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + ComputingApi *ComputingApiService + + DataApi *DataApiService + + DatacenterApi *DatacenterApiService + + ScheduleApi *ScheduleApiService + + SearchApi *SearchApiService + + StorageApi *StorageApiService + + UserApi *UserApiService + + WorkflowApi *WorkflowApiService + + WorkspaceApi *WorkspaceApiService +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.ComputingApi = (*ComputingApiService)(&c.common) + c.DataApi = (*DataApiService)(&c.common) + c.DatacenterApi = (*DatacenterApiService)(&c.common) + c.ScheduleApi = (*ScheduleApiService)(&c.common) + c.SearchApi = (*SearchApiService)(&c.common) + c.StorageApi = (*StorageApiService)(&c.common) + c.UserApi = (*UserApiService)(&c.common) + c.WorkflowApi = (*WorkflowApiService)(&c.common) + c.WorkspaceApi = (*WorkspaceApiService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } + + return fmt.Sprintf("%v", obj) +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} + +// Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath(path string) { + c.cfg.BasePath = path +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile("file", filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + } + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Override request host, if applicable + if c.cfg.Host != "" { + localVarRequest.Host = c.cfg.Host + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if strings.Contains(contentType, "application/xml") { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } else if strings.Contains(contentType, "application/json") { + if err = json.Unmarshal(b, v); err != nil { + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericSwaggerError Provides access to the body, error and model on returned errors. +type GenericSwaggerError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericSwaggerError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericSwaggerError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericSwaggerError) Model() interface{} { + return e.model +} diff --git a/api-client/oc-catalog/configuration.go b/api-client/oc-catalog/configuration.go new file mode 100644 index 0000000..2379c54 --- /dev/null +++ b/api-client/oc-catalog/configuration.go @@ -0,0 +1,73 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost/v1", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/1.0.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/api-client/oc-catalog/docs/ComputingApi.md b/api-client/oc-catalog/docs/ComputingApi.md new file mode 100644 index 0000000..f5c7461 --- /dev/null +++ b/api-client/oc-catalog/docs/ComputingApi.md @@ -0,0 +1,95 @@ +# \ComputingApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**ComputingControllerAddComputing**](ComputingApi.md#ComputingControllerAddComputing) | **Post** /computing/ | +[**ComputingControllerGetComputingByID**](ComputingApi.md#ComputingControllerGetComputingByID) | **Get** /computing/{ID} | +[**ComputingControllerGetMultipleComputingByIDs**](ComputingApi.md#ComputingControllerGetMultipleComputingByIDs) | **Get** /computing/multi/{IDs} | + + +# **ComputingControllerAddComputing** +> ComputingControllerAddComputing(ctx, body) + + +Submit a computing object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsComputingNewModel**](ModelsComputingNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ComputingControllerGetComputingByID** +> ModelsComputingModel ComputingControllerGetComputingByID(ctx, iD) + + +Find a computing resource based on ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| The ID of the resource | + +### Return type + +[**ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ComputingControllerGetMultipleComputingByIDs** +> []ModelsComputingModel ComputingControllerGetMultipleComputingByIDs(ctx, iDs) + + +Return Computing objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of computing IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/DataApi.md b/api-client/oc-catalog/docs/DataApi.md new file mode 100644 index 0000000..c968907 --- /dev/null +++ b/api-client/oc-catalog/docs/DataApi.md @@ -0,0 +1,95 @@ +# \DataApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**DataControllerCreateData**](DataApi.md#DataControllerCreateData) | **Post** /data/ | +[**DataControllerGetDataByID**](DataApi.md#DataControllerGetDataByID) | **Get** /data/{ID} | +[**DataControllerGetMultipleDataByIDs**](DataApi.md#DataControllerGetMultipleDataByIDs) | **Get** /data/multi/{IDs} | + + +# **DataControllerCreateData** +> DataControllerCreateData(ctx, body) + + +Submit data object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsDataNewModel**](ModelsDataNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DataControllerGetDataByID** +> ModelsDataModel DataControllerGetDataByID(ctx, iD) + + +Find rType data based on ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| The ID of the data resource | + +### Return type + +[**ModelsDataModel**](models.DataModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DataControllerGetMultipleDataByIDs** +> []ModelsDataModel DataControllerGetMultipleDataByIDs(ctx, iDs) + + +Return Data object if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of data IDs | + +### Return type + +[**[]ModelsDataModel**](models.DataModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/DatacenterApi.md b/api-client/oc-catalog/docs/DatacenterApi.md new file mode 100644 index 0000000..9ad47e1 --- /dev/null +++ b/api-client/oc-catalog/docs/DatacenterApi.md @@ -0,0 +1,95 @@ +# \DatacenterApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**DatacenterControllerCreateDatacenter**](DatacenterApi.md#DatacenterControllerCreateDatacenter) | **Post** /datacenter/ | +[**DatacenterControllerGetMultipleDatacentersByIDs**](DatacenterApi.md#DatacenterControllerGetMultipleDatacentersByIDs) | **Get** /datacenter/multi/{IDs} | +[**DatacenterControllerGetOneDatacenter**](DatacenterApi.md#DatacenterControllerGetOneDatacenter) | **Get** /datacenter/{ID} | + + +# **DatacenterControllerCreateDatacenter** +> DatacenterControllerCreateDatacenter(ctx, body) + + +submit Datacenter object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsDatacenterNewModel**](ModelsDatacenterNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DatacenterControllerGetMultipleDatacentersByIDs** +> []ModelsComputingModel DatacenterControllerGetMultipleDatacentersByIDs(ctx, iDs) + + +Return Datacenter objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of datacenter IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DatacenterControllerGetOneDatacenter** +> ModelsDatacenterModel DatacenterControllerGetOneDatacenter(ctx, iD) + + +find datacenter by ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| the ID you want to get | + +### Return type + +[**ModelsDatacenterModel**](models.DatacenterModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/ModelsComputingModel.md b/api-client/oc-catalog/docs/ModelsComputingModel.md new file mode 100644 index 0000000..10a7186 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsComputingModel.md @@ -0,0 +1,22 @@ +# ModelsComputingModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Dinputs** | **[]string** | | [optional] [default to null] +**Doutputs** | **[]string** | | [optional] [default to null] +**ID** | **string** | | [default to null] +**Description** | **string** | | [optional] [default to null] +**ExecutionRequirements** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**License** | **string** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | Name of the computing | [optional] [default to null] +**Owner** | **string** | | [optional] [default to null] +**Price** | **int32** | | [optional] [default to null] +**Repository** | [***ModelsRepositoryModel**](models.RepositoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsComputingNewModel.md b/api-client/oc-catalog/docs/ModelsComputingNewModel.md new file mode 100644 index 0000000..e4ccc13 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsComputingNewModel.md @@ -0,0 +1,21 @@ +# ModelsComputingNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Dinputs** | **[]string** | | [optional] [default to null] +**Doutputs** | **[]string** | | [optional] [default to null] +**Description** | **string** | | [default to null] +**ExecutionRequirements** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**License** | **string** | | [optional] [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | Name of the computing | [default to null] +**Owner** | **string** | | [optional] [default to null] +**Price** | **int32** | | [optional] [default to null] +**Repository** | [***ModelsRepositoryModel**](models.RepositoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsComputingObject.md b/api-client/oc-catalog/docs/ModelsComputingObject.md new file mode 100644 index 0000000..6b90469 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsComputingObject.md @@ -0,0 +1,13 @@ +# ModelsComputingObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DatacenterID** | **string** | Datacenter where the computing will be executed | [optional] [default to null] +**Inputs** | **[]string** | | [optional] [default to null] +**Outputs** | **[]string** | | [optional] [default to null] +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Computing model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDCstatus.md b/api-client/oc-catalog/docs/ModelsDCstatus.md new file mode 100644 index 0000000..fffee88 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDCstatus.md @@ -0,0 +1,15 @@ +# ModelsDCstatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Booked** | [***ModelsScheduleInfo**](models.ScheduleInfo.md) | | [optional] [default to null] +**DCname** | **string** | | [optional] [default to null] +**DCobjID** | **string** | | [optional] [default to null] +**ErrorMessage** | **string** | | [optional] [default to null] +**IsAvailable** | **bool** | | [optional] [default to null] +**IsReachable** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDataModel.md b/api-client/oc-catalog/docs/ModelsDataModel.md new file mode 100644 index 0000000..af5a174 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDataModel.md @@ -0,0 +1,19 @@ +# ModelsDataModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ID** | **string** | | [default to null] +**Description** | **string** | | [optional] [default to null] +**Dtype** | **string** | | [optional] [default to null] +**Example** | **string** | base64 encoded data | [optional] [default to null] +**Location** | **string** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | Name of the data | [optional] [default to null] +**Protocol** | **[]string** | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | Define type of data | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDataNewModel.md b/api-client/oc-catalog/docs/ModelsDataNewModel.md new file mode 100644 index 0000000..4ac2ff6 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDataNewModel.md @@ -0,0 +1,18 @@ +# ModelsDataNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Description** | **string** | | [default to null] +**Dtype** | **string** | | [optional] [default to null] +**Example** | **string** | base64 encoded data | [default to null] +**Location** | **string** | | [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | Name of the data | [default to null] +**Protocol** | **[]string** | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | Define type of data | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDataObject.md b/api-client/oc-catalog/docs/ModelsDataObject.md new file mode 100644 index 0000000..895e7c9 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDataObject.md @@ -0,0 +1,10 @@ +# ModelsDataObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Data model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterCpuModel.md b/api-client/oc-catalog/docs/ModelsDatacenterCpuModel.md new file mode 100644 index 0000000..94b967c --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterCpuModel.md @@ -0,0 +1,14 @@ +# ModelsDatacenterCpuModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Architecture** | **string** | | [optional] [default to null] +**Cores** | **int32** | | [default to null] +**MinimumMemory** | **int32** | | [optional] [default to null] +**Platform** | **string** | | [optional] [default to null] +**Shared** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterGpuModel.md b/api-client/oc-catalog/docs/ModelsDatacenterGpuModel.md new file mode 100644 index 0000000..e21255e --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterGpuModel.md @@ -0,0 +1,13 @@ +# ModelsDatacenterGpuModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CudaCores** | **int32** | | [optional] [default to null] +**Memory** | **int32** | Units in MB | [optional] [default to null] +**Model** | **string** | | [optional] [default to null] +**TensorCores** | **int32** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterMemoryModel.md b/api-client/oc-catalog/docs/ModelsDatacenterMemoryModel.md new file mode 100644 index 0000000..8332114 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterMemoryModel.md @@ -0,0 +1,11 @@ +# ModelsDatacenterMemoryModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Ecc** | **bool** | | [optional] [default to null] +**Size** | **int32** | Units in MB | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterModel.md b/api-client/oc-catalog/docs/ModelsDatacenterModel.md new file mode 100644 index 0000000..0e7e65b --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterModel.md @@ -0,0 +1,22 @@ +# ModelsDatacenterModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ID** | **string** | | [default to null] +**Acronym** | **string** | id of the DC | [optional] [default to null] +**BookingPrice** | **int64** | | [optional] [default to null] +**Cpu** | [***ModelsDatacenterCpuModel**](models.DatacenterCpuModel.md) | | [optional] [default to null] +**Description** | **string** | | [optional] [default to null] +**Gpu** | [**[]ModelsDatacenterGpuModel**](models.DatacenterGpuModel.md) | | [optional] [default to null] +**Hosts** | **[]string** | list of host:port | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | | [optional] [default to null] +**Owner** | **string** | | [optional] [default to null] +**Ram** | [***ModelsDatacenterMemoryModel**](models.DatacenterMemoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterNewModel.md b/api-client/oc-catalog/docs/ModelsDatacenterNewModel.md new file mode 100644 index 0000000..1ff92f9 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterNewModel.md @@ -0,0 +1,21 @@ +# ModelsDatacenterNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Acronym** | **string** | id of the DC | [default to null] +**BookingPrice** | **int64** | | [optional] [default to null] +**Cpu** | [***ModelsDatacenterCpuModel**](models.DatacenterCpuModel.md) | | [default to null] +**Description** | **string** | | [default to null] +**Gpu** | [**[]ModelsDatacenterGpuModel**](models.DatacenterGpuModel.md) | | [default to null] +**Hosts** | **[]string** | list of host:port | [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | | [default to null] +**Owner** | **string** | | [optional] [default to null] +**Ram** | [***ModelsDatacenterMemoryModel**](models.DatacenterMemoryModel.md) | | [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsDatacenterObject.md b/api-client/oc-catalog/docs/ModelsDatacenterObject.md new file mode 100644 index 0000000..ca25aaa --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsDatacenterObject.md @@ -0,0 +1,10 @@ +# ModelsDatacenterObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Data model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsExecutionRequirementsModel.md b/api-client/oc-catalog/docs/ModelsExecutionRequirementsModel.md new file mode 100644 index 0000000..c2e2851 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsExecutionRequirementsModel.md @@ -0,0 +1,15 @@ +# ModelsExecutionRequirementsModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Cpus** | **int32** | | [default to null] +**DiskIo** | **string** | | [optional] [default to null] +**Gpus** | **int32** | Amount of GPUs needed | [optional] [default to null] +**Parallel** | **bool** | | [optional] [default to null] +**Ram** | **int32** | Units in MB | [default to null] +**ScalingModel** | **int32** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsRepositoryModel.md b/api-client/oc-catalog/docs/ModelsRepositoryModel.md new file mode 100644 index 0000000..046f8f1 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsRepositoryModel.md @@ -0,0 +1,11 @@ +# ModelsRepositoryModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Credentials** | **string** | | [optional] [default to null] +**Url** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsScheduleDb.md b/api-client/oc-catalog/docs/ModelsScheduleDb.md new file mode 100644 index 0000000..1a3cd78 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsScheduleDb.md @@ -0,0 +1,13 @@ +# ModelsScheduleDb + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ResourceQty** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**StartDate** | **string** | | [optional] [default to null] +**StopDate** | **string** | | [optional] [default to null] +**Workflow** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsScheduleInfo.md b/api-client/oc-catalog/docs/ModelsScheduleInfo.md new file mode 100644 index 0000000..7df98b1 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsScheduleInfo.md @@ -0,0 +1,11 @@ +# ModelsScheduleInfo + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**NextExecutions** | **[]string** | | [optional] [default to null] +**Total** | **int64** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsScheduleTime.md b/api-client/oc-catalog/docs/ModelsScheduleTime.md new file mode 100644 index 0000000..f6d5731 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsScheduleTime.md @@ -0,0 +1,9 @@ +# ModelsScheduleTime + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsSearchResult.md b/api-client/oc-catalog/docs/ModelsSearchResult.md new file mode 100644 index 0000000..e064143 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsSearchResult.md @@ -0,0 +1,13 @@ +# ModelsSearchResult + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Computing** | [**[]ModelsComputingModel**](models.ComputingModel.md) | | [default to null] +**Data** | [**[]ModelsDataModel**](models.DataModel.md) | | [optional] [default to null] +**Datacenter** | [**[]ModelsDatacenterModel**](models.DatacenterModel.md) | | [optional] [default to null] +**Storage** | [**[]ModelsStorageModel**](models.StorageModel.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsStorageModel.md b/api-client/oc-catalog/docs/ModelsStorageModel.md new file mode 100644 index 0000000..67da13e --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsStorageModel.md @@ -0,0 +1,21 @@ +# ModelsStorageModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DCacronym** | **string** | Unique ID of the DC where it is the storage | [optional] [default to null] +**ID** | **string** | | [default to null] +**BookingPrice** | **int32** | | [optional] [default to null] +**Description** | **string** | | [optional] [default to null] +**Encryption** | **bool** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | | [optional] [default to null] +**Redundancy** | **string** | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Size** | **int32** | | [optional] [default to null] +**Throughput** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsStorageNewModel.md b/api-client/oc-catalog/docs/ModelsStorageNewModel.md new file mode 100644 index 0000000..ee1d552 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsStorageNewModel.md @@ -0,0 +1,20 @@ +# ModelsStorageNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DCacronym** | **string** | Unique ID of the DC where it is the storage | [default to null] +**BookingPrice** | **int32** | | [optional] [default to null] +**Description** | **string** | | [default to null] +**Encryption** | **bool** | | [optional] [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | | [default to null] +**Redundancy** | **string** | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Size** | **int32** | | [default to null] +**Throughput** | **string** | | [optional] [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsStorageObject.md b/api-client/oc-catalog/docs/ModelsStorageObject.md new file mode 100644 index 0000000..244eab5 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsStorageObject.md @@ -0,0 +1,12 @@ +# ModelsStorageObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Inputs** | **[]string** | | [optional] [default to null] +**Outputs** | **[]string** | | [optional] [default to null] +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Storage model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsWorkflow.md b/api-client/oc-catalog/docs/ModelsWorkflow.md new file mode 100644 index 0000000..f023ba2 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsWorkflow.md @@ -0,0 +1,15 @@ +# ModelsWorkflow + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**MxgraphXML** | **string** | State of the mxgraph | [optional] [default to null] +**Computing** | [***ModelsComputingObject**](models.ComputingObject.md) | | [optional] [default to null] +**Data** | [***ModelsDataObject**](models.DataObject.md) | | [optional] [default to null] +**Datacenter** | [***ModelsDatacenterObject**](models.DatacenterObject.md) | | [optional] [default to null] +**Schedules** | [***ModelsWorkflowSchedule**](models.WorkflowSchedule.md) | | [optional] [default to null] +**Storage** | [***ModelsStorageObject**](models.StorageObject.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsWorkflowSchedule.md b/api-client/oc-catalog/docs/ModelsWorkflowSchedule.md new file mode 100644 index 0000000..516c913 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsWorkflowSchedule.md @@ -0,0 +1,16 @@ +# ModelsWorkflowSchedule + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**IsService** | **bool** | Service: true, Task: false | [optional] [default to null] +**StartDate** | **string** | | [optional] [default to null] +**StopDate** | **string** | | [optional] [default to null] +**Cron** | **string** | | [optional] [default to null] +**Duration** | **int32** | Durantion in seconds | [optional] [default to null] +**Events** | **string** | | [optional] [default to null] +**IsBooked** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsWorkspace.md b/api-client/oc-catalog/docs/ModelsWorkspace.md new file mode 100644 index 0000000..d7f36b9 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsWorkspace.md @@ -0,0 +1,15 @@ +# ModelsWorkspace + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Workflows** | [***ModelsWorkflow**](models.Workflow.md) | | [optional] [default to null] +**Computing** | **[]string** | | [optional] [default to null] +**Data** | **[]string** | | [optional] [default to null] +**Datacenter** | **[]string** | | [optional] [default to null] +**Storage** | **[]string** | | [optional] [default to null] +**UserId** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ModelsWorkspaceModel.md b/api-client/oc-catalog/docs/ModelsWorkspaceModel.md new file mode 100644 index 0000000..bf646a1 --- /dev/null +++ b/api-client/oc-catalog/docs/ModelsWorkspaceModel.md @@ -0,0 +1,14 @@ +# ModelsWorkspaceModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Computing** | [**[]ModelsComputingModel**](models.ComputingModel.md) | | [optional] [default to null] +**Data** | [**[]ModelsDataModel**](models.DataModel.md) | | [optional] [default to null] +**Datacenter** | [**[]ModelsDatacenterModel**](models.DatacenterModel.md) | | [optional] [default to null] +**Storage** | [**[]ModelsStorageModel**](models.StorageModel.md) | | [optional] [default to null] +**UserId** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/PrimitiveObjectId.md b/api-client/oc-catalog/docs/PrimitiveObjectId.md new file mode 100644 index 0000000..011ef59 --- /dev/null +++ b/api-client/oc-catalog/docs/PrimitiveObjectId.md @@ -0,0 +1,9 @@ +# PrimitiveObjectId + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/ScheduleApi.md b/api-client/oc-catalog/docs/ScheduleApi.md new file mode 100644 index 0000000..b3d7dcd --- /dev/null +++ b/api-client/oc-catalog/docs/ScheduleApi.md @@ -0,0 +1,164 @@ +# \ScheduleApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC**](ScheduleApi.md#ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC) | **Post** /schedule/check | +[**ScheduleControllerCreateSchedule**](ScheduleApi.md#ScheduleControllerCreateSchedule) | **Post** /schedule/book | +[**ScheduleControllerGetNextSchedule**](ScheduleApi.md#ScheduleControllerGetNextSchedule) | **Get** /schedule/next | +[**ScheduleControllerGetPreviousSchedule**](ScheduleApi.md#ScheduleControllerGetPreviousSchedule) | **Get** /schedule/previous | +[**ScheduleControllerGetSchedules**](ScheduleApi.md#ScheduleControllerGetSchedules) | **Get** /schedule/ | + + +# **ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC** +> ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC(ctx, cron, duration, startDate, stopDate, requirements) + + +Check for availability of this DC + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **cron** | **string**| Cron syntax | + **duration** | **int32**| Duration in seconds | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **requirements** | [**ModelsExecutionRequirementsModel**](ModelsExecutionRequirementsModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerCreateSchedule** +> ModelsScheduleInfo ScheduleControllerCreateSchedule(ctx, dcName, workflowName, cron, duration, startDate, stopDate, requirements) + + +Create schedule for a workflow. It will return some future executions just as information + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **dcName** | **string**| Name of the node (oc-catalog) from where the workflow comes. | + **workflowName** | **string**| Workflow Name | + **cron** | **string**| Cron syntax with year. If no year is specified, will use the current | + **duration** | **int32**| Duration in seconds | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **requirements** | [**ModelsExecutionRequirementsModel**](ModelsExecutionRequirementsModel.md)| The object content | + +### Return type + +[**ModelsScheduleInfo**](models.ScheduleInfo.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetNextSchedule** +> TimeTime ScheduleControllerGetNextSchedule(ctx, baseDate) + + +Give a date, get the next date where there are at least on schedule. If no hours specified, will assume 00:00 + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **baseDate** | [**interface{}**](.md)| Base date | + +### Return type + +[**TimeTime**](*time.Time.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetPreviousSchedule** +> TimeTime ScheduleControllerGetPreviousSchedule(ctx, baseDate) + + +Give a date, get the previous date where there are at least on schedule. If no hours specified, will assume 00:00 + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **baseDate** | [**interface{}**](.md)| Base date | + +### Return type + +[**TimeTime**](*time.Time.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetSchedules** +> []ModelsScheduleDb ScheduleControllerGetSchedules(ctx, startDate, stopDate) + + +Get a list of next startDates schedules (inclusive). If timezone is not specified, will assume UTC + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **startDate** | [**interface{}**](.md)| Start date | + **stopDate** | [**interface{}**](.md)| End date | + +### Return type + +[**[]ModelsScheduleDb**](models.ScheduleDB.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/SearchApi.md b/api-client/oc-catalog/docs/SearchApi.md new file mode 100644 index 0000000..a030b31 --- /dev/null +++ b/api-client/oc-catalog/docs/SearchApi.md @@ -0,0 +1,37 @@ +# \SearchApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**SearchControllerSearchByWord**](SearchApi.md#SearchControllerSearchByWord) | **Get** /search/byWord | + + +# **SearchControllerSearchByWord** +> ModelsSearchResult SearchControllerSearchByWord(ctx, word) + + +find resources by word + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **word** | **string**| Word to search across all resources | + +### Return type + +[**ModelsSearchResult**](models.SearchResult.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/StorageApi.md b/api-client/oc-catalog/docs/StorageApi.md new file mode 100644 index 0000000..ee30178 --- /dev/null +++ b/api-client/oc-catalog/docs/StorageApi.md @@ -0,0 +1,95 @@ +# \StorageApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**StorageControllerCreateStorage**](StorageApi.md#StorageControllerCreateStorage) | **Post** /storage/ | +[**StorageControllerGet**](StorageApi.md#StorageControllerGet) | **Get** /storage/{ID} | +[**StorageControllerGetMultipleStoragesByIDs**](StorageApi.md#StorageControllerGetMultipleStoragesByIDs) | **Get** /storage/multi/{IDs} | + + +# **StorageControllerCreateStorage** +> StorageControllerCreateStorage(ctx, body) + + +submit storage object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsStorageNewModel**](ModelsStorageNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **StorageControllerGet** +> ModelsStorageModel StorageControllerGet(ctx, iD) + + +find storage by ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| the ID you want to get | + +### Return type + +[**ModelsStorageModel**](models.StorageModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **StorageControllerGetMultipleStoragesByIDs** +> []ModelsComputingModel StorageControllerGetMultipleStoragesByIDs(ctx, iDs) + + +Return Storage objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of storage IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/TimeTime.md b/api-client/oc-catalog/docs/TimeTime.md new file mode 100644 index 0000000..f1b1d48 --- /dev/null +++ b/api-client/oc-catalog/docs/TimeTime.md @@ -0,0 +1,9 @@ +# TimeTime + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/api-client/oc-catalog/docs/UserApi.md b/api-client/oc-catalog/docs/UserApi.md new file mode 100644 index 0000000..8c03310 --- /dev/null +++ b/api-client/oc-catalog/docs/UserApi.md @@ -0,0 +1,63 @@ +# \UserApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**UserControllerLogin**](UserApi.md#UserControllerLogin) | **Get** /user/login | +[**UserControllerLogout**](UserApi.md#UserControllerLogout) | **Get** /user/logout | + + +# **UserControllerLogin** +> UserControllerLogin(ctx, username, password) + + +Logs user into the system + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **username** | **string**| The username for login | + **password** | **string**| The password for login | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **UserControllerLogout** +> UserControllerLogout(ctx, ) + + +Logs out current logged in user session + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/WorkflowApi.md b/api-client/oc-catalog/docs/WorkflowApi.md new file mode 100644 index 0000000..ad2177e --- /dev/null +++ b/api-client/oc-catalog/docs/WorkflowApi.md @@ -0,0 +1,345 @@ +# \WorkflowApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**WorkflowControllerAddNewObjectToAWorkflow**](WorkflowApi.md#WorkflowControllerAddNewObjectToAWorkflow) | **Post** /workflow/{workflowName}/add | +[**WorkflowControllerBookSchedule**](WorkflowApi.md#WorkflowControllerBookSchedule) | **Post** /workflow/{workflowName}/schedule/book | +[**WorkflowControllerCheckSchedule**](WorkflowApi.md#WorkflowControllerCheckSchedule) | **Get** /workflow/{workflowName}/schedule/check | +[**WorkflowControllerCreateANewWorkflow**](WorkflowApi.md#WorkflowControllerCreateANewWorkflow) | **Post** /workflow/ | +[**WorkflowControllerCreateARealtionshipBetweenTwoRobjects**](WorkflowApi.md#WorkflowControllerCreateARealtionshipBetweenTwoRobjects) | **Post** /workflow/{workflowName}/link | +[**WorkflowControllerGetMxGraphLastStatus**](WorkflowApi.md#WorkflowControllerGetMxGraphLastStatus) | **Get** /workflow/{workflowName}/mxGraphParser | +[**WorkflowControllerGetSchedule**](WorkflowApi.md#WorkflowControllerGetSchedule) | **Get** /workflow/{workflowName}/schedule | +[**WorkflowControllerGetWorkflow**](WorkflowApi.md#WorkflowControllerGetWorkflow) | **Get** /workflow/{workflowName} | +[**WorkflowControllerListWorkflows**](WorkflowApi.md#WorkflowControllerListWorkflows) | **Get** /workflow/ | +[**WorkflowControllerParseMxGraph**](WorkflowApi.md#WorkflowControllerParseMxGraph) | **Post** /workflow/{workflowName}/mxGraphParser | +[**WorkflowControllerSetSchedule**](WorkflowApi.md#WorkflowControllerSetSchedule) | **Put** /workflow/{workflowName}/schedule | + + +# **WorkflowControllerAddNewObjectToAWorkflow** +> WorkflowControllerAddNewObjectToAWorkflow(ctx, workflowName, rID) + + +Create a Rtype object from already added resources to the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| workflow Name | + **rID** | **string**| rID of already existing item in Workspace | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerBookSchedule** +> []ModelsDCstatus WorkflowControllerBookSchedule(ctx, workflowName) + + +Book a schedule in all DCs of the workflow. Must set a desired schedule first! + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**[]ModelsDCstatus**](models.DCstatus.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCheckSchedule** +> []ModelsDCstatus WorkflowControllerCheckSchedule(ctx, workflowName) + + +Check if we can schedule the project in other DCs. Must set a desired schedule first! + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**[]ModelsDCstatus**](models.DCstatus.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCreateANewWorkflow** +> WorkflowControllerCreateANewWorkflow(ctx, workflowName) + + +Create a name for the new workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Name of the workflow | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCreateARealtionshipBetweenTwoRobjects** +> WorkflowControllerCreateARealtionshipBetweenTwoRobjects(ctx, workflowName, rObjIDsource, isInput, rObjIDtarger) + + +Create a Rtype object from already added resources to the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **rObjIDsource** | **string**| Robject source. Usually Data | + **isInput** | **bool**| If the operation is for input (true) linkage or output (false) | + **rObjIDtarger** | **string**| Robject where will be written the association | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetMxGraphLastStatus** +> WorkflowControllerGetMxGraphLastStatus(ctx, workflowName) + + +Obtain the last mxgraph XML status from the workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetSchedule** +> ModelsScheduleTime WorkflowControllerGetSchedule(ctx, workflowName) + + +Obtain the desired schedule of this workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**ModelsScheduleTime**](models.ScheduleTime.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetWorkflow** +> ModelsWorkflow WorkflowControllerGetWorkflow(ctx, workflowName) + + +Get a workflow by name + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**ModelsWorkflow**](models.Workflow.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerListWorkflows** +> WorkflowControllerListWorkflows(ctx, ) + + +List available workflows + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerParseMxGraph** +> WorkflowControllerParseMxGraph(ctx, workflowName, xmlData) + + +If we use this aproach to transofrm mxgraph representation in our representation, we should not use other API calls for modify the project structure or we'll have inconsistencies. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **xmlData** | **string**| Xml representation of the workflow | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerSetSchedule** +> ModelsScheduleInfo WorkflowControllerSetSchedule(ctx, workflowName, isService, startDate, stopDate, optional) + + +Set desired schedule by the user. No other effects a part of saving the user input + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **isService** | **bool**| True: Service, False: Task | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **optional** | ***WorkflowApiWorkflowControllerSetScheduleOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a WorkflowApiWorkflowControllerSetScheduleOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + + + + **events** | **optional.String**| List of events separated by comma | + **cronString** | **optional.String**| Cron string | + **duration** | **optional.Int32**| Duration in seconds | + +### Return type + +[**ModelsScheduleInfo**](models.ScheduleInfo.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/docs/WorkspaceApi.md b/api-client/oc-catalog/docs/WorkspaceApi.md new file mode 100644 index 0000000..0bc2049 --- /dev/null +++ b/api-client/oc-catalog/docs/WorkspaceApi.md @@ -0,0 +1,118 @@ +# \WorkspaceApi + +All URIs are relative to *https://localhost/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**WorkspaceControllerAddModelToWorkspace**](WorkspaceApi.md#WorkspaceControllerAddModelToWorkspace) | **Post** /workspace/ | +[**WorkspaceControllerDeleteElementFromUserWorkspace**](WorkspaceApi.md#WorkspaceControllerDeleteElementFromUserWorkspace) | **Delete** /workspace/ | +[**WorkspaceControllerGetFullWorkspace**](WorkspaceApi.md#WorkspaceControllerGetFullWorkspace) | **Get** /workspace/list_model | +[**WorkspaceControllerGetWorkspace**](WorkspaceApi.md#WorkspaceControllerGetWorkspace) | **Get** /workspace/list | + + +# **WorkspaceControllerAddModelToWorkspace** +> WorkspaceControllerAddModelToWorkspace(ctx, id, rtype) + + +Insert a resource in the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **id** | **string**| ID of a resource | + **rtype** | **string**| Type of resource | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerDeleteElementFromUserWorkspace** +> WorkspaceControllerDeleteElementFromUserWorkspace(ctx, id, rtype) + + +Remove a resource from the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **id** | **string**| ID of a resource | + **rtype** | **string**| Type of resource | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerGetFullWorkspace** +> ModelsWorkspaceModel WorkspaceControllerGetFullWorkspace(ctx, ) + + +Get full workspace elements based on user_id token + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + +[**ModelsWorkspaceModel**](models.WorkspaceModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerGetWorkspace** +> ModelsWorkspace WorkspaceControllerGetWorkspace(ctx, ) + + +Get workspace elements based on user_id token + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + +[**ModelsWorkspace**](models.Workspace.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/api-client/oc-catalog/git_push.sh b/api-client/oc-catalog/git_push.sh new file mode 100644 index 0000000..ae01b18 --- /dev/null +++ b/api-client/oc-catalog/git_push.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/api-client/oc-catalog/model_models_computing_model.go b/api-client/oc-catalog/model_models_computing_model.go new file mode 100644 index 0000000..749c2ab --- /dev/null +++ b/api-client/oc-catalog/model_models_computing_model.go @@ -0,0 +1,28 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingModel struct { + Dinputs []string `json:"Dinputs,omitempty"` + Doutputs []string `json:"Doutputs,omitempty"` + ID string `json:"ID"` + Description string `json:"description,omitempty"` + ExecutionRequirements *ModelsExecutionRequirementsModel `json:"execution_requirements,omitempty"` + License string `json:"license,omitempty"` + Logo string `json:"logo,omitempty"` + // Name of the computing + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Price int32 `json:"price,omitempty"` + Repository *ModelsRepositoryModel `json:"repository,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_computing_new_model.go b/api-client/oc-catalog/model_models_computing_new_model.go new file mode 100644 index 0000000..06f41f2 --- /dev/null +++ b/api-client/oc-catalog/model_models_computing_new_model.go @@ -0,0 +1,27 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingNewModel struct { + Dinputs []string `json:"Dinputs,omitempty"` + Doutputs []string `json:"Doutputs,omitempty"` + Description string `json:"description"` + ExecutionRequirements *ModelsExecutionRequirementsModel `json:"execution_requirements,omitempty"` + License string `json:"license,omitempty"` + Logo string `json:"logo"` + // Name of the computing + Name string `json:"name"` + Owner string `json:"owner,omitempty"` + Price int32 `json:"price,omitempty"` + Repository *ModelsRepositoryModel `json:"repository,omitempty"` + ShortDescription string `json:"short_description"` + Type_ string `json:"type"` +} diff --git a/api-client/oc-catalog/model_models_computing_object.go b/api-client/oc-catalog/model_models_computing_object.go new file mode 100644 index 0000000..ba7415c --- /dev/null +++ b/api-client/oc-catalog/model_models_computing_object.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingObject struct { + // Datacenter where the computing will be executed + DatacenterID string `json:"datacenterID,omitempty"` + Inputs []string `json:"inputs,omitempty"` + Outputs []string `json:"outputs,omitempty"` + // Computing model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_d_cstatus.go b/api-client/oc-catalog/model_models_d_cstatus.go new file mode 100644 index 0000000..6d06e2a --- /dev/null +++ b/api-client/oc-catalog/model_models_d_cstatus.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDCstatus struct { + Booked *ModelsScheduleInfo `json:"Booked,omitempty"` + DCname string `json:"DCname,omitempty"` + DCobjID string `json:"DCobjID,omitempty"` + ErrorMessage string `json:"ErrorMessage,omitempty"` + IsAvailable bool `json:"IsAvailable,omitempty"` + IsReachable bool `json:"IsReachable,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_data_model.go b/api-client/oc-catalog/model_models_data_model.go new file mode 100644 index 0000000..d5c0866 --- /dev/null +++ b/api-client/oc-catalog/model_models_data_model.go @@ -0,0 +1,27 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataModel struct { + ID string `json:"ID"` + Description string `json:"description,omitempty"` + Dtype string `json:"dtype,omitempty"` + // base64 encoded data + Example string `json:"example,omitempty"` + Location string `json:"location,omitempty"` + Logo string `json:"logo,omitempty"` + // Name of the data + Name string `json:"name,omitempty"` + Protocol []string `json:"protocol,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + // Define type of data + Type_ string `json:"type,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_data_new_model.go b/api-client/oc-catalog/model_models_data_new_model.go new file mode 100644 index 0000000..bd9922a --- /dev/null +++ b/api-client/oc-catalog/model_models_data_new_model.go @@ -0,0 +1,26 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataNewModel struct { + Description string `json:"description"` + Dtype string `json:"dtype,omitempty"` + // base64 encoded data + Example string `json:"example"` + Location string `json:"location"` + Logo string `json:"logo"` + // Name of the data + Name string `json:"name"` + Protocol []string `json:"protocol,omitempty"` + ShortDescription string `json:"short_description"` + // Define type of data + Type_ string `json:"type"` +} diff --git a/api-client/oc-catalog/model_models_data_object.go b/api-client/oc-catalog/model_models_data_object.go new file mode 100644 index 0000000..8c15b71 --- /dev/null +++ b/api-client/oc-catalog/model_models_data_object.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataObject struct { + // Data model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_cpu_model.go b/api-client/oc-catalog/model_models_datacenter_cpu_model.go new file mode 100644 index 0000000..70ba71d --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_cpu_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterCpuModel struct { + Architecture string `json:"architecture,omitempty"` + Cores int32 `json:"cores"` + MinimumMemory int32 `json:"minimum_memory,omitempty"` + Platform string `json:"platform,omitempty"` + Shared bool `json:"shared,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_gpu_model.go b/api-client/oc-catalog/model_models_datacenter_gpu_model.go new file mode 100644 index 0000000..2a7455b --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_gpu_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterGpuModel struct { + CudaCores int32 `json:"cuda_cores,omitempty"` + // Units in MB + Memory int32 `json:"memory,omitempty"` + Model string `json:"model,omitempty"` + TensorCores int32 `json:"tensor_cores,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_memory_model.go b/api-client/oc-catalog/model_models_datacenter_memory_model.go new file mode 100644 index 0000000..4d7d2e1 --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_memory_model.go @@ -0,0 +1,17 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterMemoryModel struct { + Ecc bool `json:"ecc,omitempty"` + // Units in MB + Size int32 `json:"size,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_model.go b/api-client/oc-catalog/model_models_datacenter_model.go new file mode 100644 index 0000000..10d9c69 --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_model.go @@ -0,0 +1,29 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterModel struct { + ID string `json:"ID"` + // id of the DC + Acronym string `json:"acronym,omitempty"` + BookingPrice int64 `json:"bookingPrice,omitempty"` + Cpu *ModelsDatacenterCpuModel `json:"cpu,omitempty"` + Description string `json:"description,omitempty"` + Gpu []ModelsDatacenterGpuModel `json:"gpu,omitempty"` + // list of host:port + Hosts []string `json:"hosts,omitempty"` + Logo string `json:"logo,omitempty"` + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Ram *ModelsDatacenterMemoryModel `json:"ram,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_new_model.go b/api-client/oc-catalog/model_models_datacenter_new_model.go new file mode 100644 index 0000000..40c36e2 --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_new_model.go @@ -0,0 +1,28 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterNewModel struct { + // id of the DC + Acronym string `json:"acronym"` + BookingPrice int64 `json:"bookingPrice,omitempty"` + Cpu *ModelsDatacenterCpuModel `json:"cpu"` + Description string `json:"description"` + Gpu []ModelsDatacenterGpuModel `json:"gpu"` + // list of host:port + Hosts []string `json:"hosts"` + Logo string `json:"logo"` + Name string `json:"name"` + Owner string `json:"owner,omitempty"` + Ram *ModelsDatacenterMemoryModel `json:"ram"` + ShortDescription string `json:"short_description"` + Type_ string `json:"type"` +} diff --git a/api-client/oc-catalog/model_models_datacenter_object.go b/api-client/oc-catalog/model_models_datacenter_object.go new file mode 100644 index 0000000..b8faa1d --- /dev/null +++ b/api-client/oc-catalog/model_models_datacenter_object.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterObject struct { + // Data model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_execution_requirements_model.go b/api-client/oc-catalog/model_models_execution_requirements_model.go new file mode 100644 index 0000000..80a2f32 --- /dev/null +++ b/api-client/oc-catalog/model_models_execution_requirements_model.go @@ -0,0 +1,22 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsExecutionRequirementsModel struct { + Cpus int32 `json:"cpus"` + DiskIo string `json:"disk_io,omitempty"` + // Amount of GPUs needed + Gpus int32 `json:"gpus,omitempty"` + Parallel bool `json:"parallel,omitempty"` + // Units in MB + Ram int32 `json:"ram"` + ScalingModel int32 `json:"scaling_model,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_repository_model.go b/api-client/oc-catalog/model_models_repository_model.go new file mode 100644 index 0000000..74e2d30 --- /dev/null +++ b/api-client/oc-catalog/model_models_repository_model.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsRepositoryModel struct { + Credentials string `json:"credentials,omitempty"` + Url string `json:"url,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_schedule_db.go b/api-client/oc-catalog/model_models_schedule_db.go new file mode 100644 index 0000000..3bc7c83 --- /dev/null +++ b/api-client/oc-catalog/model_models_schedule_db.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleDb struct { + ResourceQty *ModelsExecutionRequirementsModel `json:"ResourceQty,omitempty"` + StartDate string `json:"StartDate,omitempty"` + StopDate string `json:"StopDate,omitempty"` + Workflow string `json:"Workflow,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_schedule_info.go b/api-client/oc-catalog/model_models_schedule_info.go new file mode 100644 index 0000000..d09b0d4 --- /dev/null +++ b/api-client/oc-catalog/model_models_schedule_info.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleInfo struct { + NextExecutions []string `json:"NextExecutions,omitempty"` + Total int64 `json:"Total,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_schedule_time.go b/api-client/oc-catalog/model_models_schedule_time.go new file mode 100644 index 0000000..d478e5c --- /dev/null +++ b/api-client/oc-catalog/model_models_schedule_time.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleTime struct { +} diff --git a/api-client/oc-catalog/model_models_search_result.go b/api-client/oc-catalog/model_models_search_result.go new file mode 100644 index 0000000..3e7eb81 --- /dev/null +++ b/api-client/oc-catalog/model_models_search_result.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsSearchResult struct { + Computing []ModelsComputingModel `json:"computing"` + Data []ModelsDataModel `json:"data,omitempty"` + Datacenter []ModelsDatacenterModel `json:"datacenter,omitempty"` + Storage []ModelsStorageModel `json:"storage,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_storage_model.go b/api-client/oc-catalog/model_models_storage_model.go new file mode 100644 index 0000000..ff86aa0 --- /dev/null +++ b/api-client/oc-catalog/model_models_storage_model.go @@ -0,0 +1,27 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageModel struct { + // Unique ID of the DC where it is the storage + DCacronym string `json:"DCacronym,omitempty"` + ID string `json:"ID"` + BookingPrice int32 `json:"bookingPrice,omitempty"` + Description string `json:"description,omitempty"` + Encryption bool `json:"encryption,omitempty"` + Logo string `json:"logo,omitempty"` + Name string `json:"name,omitempty"` + Redundancy string `json:"redundancy,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Size int32 `json:"size,omitempty"` + Throughput string `json:"throughput,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_storage_new_model.go b/api-client/oc-catalog/model_models_storage_new_model.go new file mode 100644 index 0000000..b7c5709 --- /dev/null +++ b/api-client/oc-catalog/model_models_storage_new_model.go @@ -0,0 +1,26 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageNewModel struct { + // Unique ID of the DC where it is the storage + DCacronym string `json:"DCacronym"` + BookingPrice int32 `json:"bookingPrice,omitempty"` + Description string `json:"description"` + Encryption bool `json:"encryption,omitempty"` + Logo string `json:"logo"` + Name string `json:"name"` + Redundancy string `json:"redundancy,omitempty"` + ShortDescription string `json:"short_description"` + Size int32 `json:"size"` + Throughput string `json:"throughput,omitempty"` + Type_ string `json:"type"` +} diff --git a/api-client/oc-catalog/model_models_storage_object.go b/api-client/oc-catalog/model_models_storage_object.go new file mode 100644 index 0000000..6996dda --- /dev/null +++ b/api-client/oc-catalog/model_models_storage_object.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageObject struct { + Inputs []string `json:"inputs,omitempty"` + Outputs []string `json:"outputs,omitempty"` + // Storage model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_workflow.go b/api-client/oc-catalog/model_models_workflow.go new file mode 100644 index 0000000..8758c96 --- /dev/null +++ b/api-client/oc-catalog/model_models_workflow.go @@ -0,0 +1,21 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkflow struct { + // State of the mxgraph + MxgraphXML string `json:"MxgraphXML,omitempty"` + Computing *ModelsComputingObject `json:"computing,omitempty"` + Data *ModelsDataObject `json:"data,omitempty"` + Datacenter *ModelsDatacenterObject `json:"datacenter,omitempty"` + Schedules *ModelsWorkflowSchedule `json:"schedules,omitempty"` + Storage *ModelsStorageObject `json:"storage,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_workflow_schedule.go b/api-client/oc-catalog/model_models_workflow_schedule.go new file mode 100644 index 0000000..e1eda14 --- /dev/null +++ b/api-client/oc-catalog/model_models_workflow_schedule.go @@ -0,0 +1,23 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkflowSchedule struct { + // Service: true, Task: false + IsService bool `json:"IsService,omitempty"` + StartDate string `json:"StartDate,omitempty"` + StopDate string `json:"StopDate,omitempty"` + Cron string `json:"cron,omitempty"` + // Durantion in seconds + Duration int32 `json:"duration,omitempty"` + Events string `json:"events,omitempty"` + IsBooked bool `json:"isBooked,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_workspace.go b/api-client/oc-catalog/model_models_workspace.go new file mode 100644 index 0000000..0be126d --- /dev/null +++ b/api-client/oc-catalog/model_models_workspace.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkspace struct { + Workflows *ModelsWorkflow `json:"Workflows,omitempty"` + Computing []string `json:"computing,omitempty"` + Data []string `json:"data,omitempty"` + Datacenter []string `json:"datacenter,omitempty"` + Storage []string `json:"storage,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/api-client/oc-catalog/model_models_workspace_model.go b/api-client/oc-catalog/model_models_workspace_model.go new file mode 100644 index 0000000..f78c412 --- /dev/null +++ b/api-client/oc-catalog/model_models_workspace_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkspaceModel struct { + Computing []ModelsComputingModel `json:"computing,omitempty"` + Data []ModelsDataModel `json:"data,omitempty"` + Datacenter []ModelsDatacenterModel `json:"datacenter,omitempty"` + Storage []ModelsStorageModel `json:"storage,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/api-client/oc-catalog/model_primitive_object_id.go b/api-client/oc-catalog/model_primitive_object_id.go new file mode 100644 index 0000000..f52a0cb --- /dev/null +++ b/api-client/oc-catalog/model_primitive_object_id.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type PrimitiveObjectId struct { +} diff --git a/api-client/oc-catalog/model_time_time.go b/api-client/oc-catalog/model_time_time.go new file mode 100644 index 0000000..572d650 --- /dev/null +++ b/api-client/oc-catalog/model_time_time.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type TimeTime struct { +} diff --git a/api-client/oc-catalog/response.go b/api-client/oc-catalog/response.go new file mode 100644 index 0000000..d8927be --- /dev/null +++ b/api-client/oc-catalog/response.go @@ -0,0 +1,44 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: valentin.kivachuk@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "net/http" +) + +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the swagger operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/conf/app.conf b/conf/app.conf new file mode 100644 index 0000000..d4e8a91 --- /dev/null +++ b/conf/app.conf @@ -0,0 +1,5 @@ +appname = search +httpport = 8080 +runmode = dev + +EnableDocs = true \ No newline at end of file diff --git a/controllers/datacenter.go b/controllers/datacenter.go new file mode 100644 index 0000000..cc90004 --- /dev/null +++ b/controllers/datacenter.go @@ -0,0 +1,21 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// DatacenterController is the controller in chrage of solr +type DatacenterController struct { + beego.Controller +} + +// Prepare checks for logged in Datacenter +func (c *DatacenterController) Prepare() { + +} + +// Get implements simple solr Datacenter +func (c *DatacenterController) Get() { + + c.TplName = "datacenter.tpl" +} diff --git a/controllers/default.go b/controllers/default.go new file mode 100644 index 0000000..afcca0c --- /dev/null +++ b/controllers/default.go @@ -0,0 +1,31 @@ +package controllers + +import ( + OCCatalog_cli "oc-search/api-client/oc-catalog" + + beego "github.com/beego/beego/v2/server/web" +) + +// MainController is in charge of the main page +type MainController struct { + beego.Controller +} + +var OCCatalogAPI *OCCatalog_cli.APIClient + +func init() { + // auth := context.WithValue(context.Background(), OCCatalog_cli.ContextAPIKey, OCCatalog_cli.APIKey{Key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE2MTI1OTI3MDIsInVzZXJfaWQiOiJ4ZCJ9.kTLb1FtpdnaobUpe5u9Jw8S7Cc6gf7ExmU4U3XMcC2o"}) + OCCatalogAPI = OCCatalog_cli.NewAPIClient(&OCCatalog_cli.Configuration{BasePath: "http://localhost:49618/v1", + DefaultHeader: map[string]string{ + "authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE2MTMwMDI0NjAsInVzZXJfaWQiOiJhc2QifQ.TXT18aeulnCrtedKKFVaD0BapOTdVAFcJJdVS7zk0I8", + }, + }) + //TODO: Test API is reachable or throw exception +} + +// Get provides the main page +func (c *MainController) Get() { + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "index.tpl" +} diff --git a/controllers/details.go b/controllers/details.go new file mode 100644 index 0000000..3083b3a --- /dev/null +++ b/controllers/details.go @@ -0,0 +1,74 @@ +package controllers + +import ( + "context" + + beego "github.com/beego/beego/v2/server/web" +) + +// DetailsController is the controller in chrage of solr +type DetailsController struct { + beego.Controller +} + +// @Title GetDetails +// @Description Return details of a resource +// @Success 200 {string} Cool +// @router /data/:id [post] +// @router /computing/:id [post] +// @router /storage/:id [post] +// @router /datacenter/:id [post] +func (c *DetailsController) SubmitToWorkspace(id string) { + rType := c.Ctx.Input.Params()["1"] + + var err error + + switch rType { + case "data": + case "computing": + case "datacenter": + case "storage": + _, err = OCCatalogAPI.WorkspaceApi.WorkspaceControllerAddModelToWorkspace(context.Background(), id, rType) + default: + return + } + + if err != nil { + return + } + +} + +// @Title GetDetails +// @Description Return details of a resource +// @Success 200 {string} Cool +// @router /data/:id [get] +// @router /computing/:id [get] +// @router /storage/:id [get] +// @router /datacenter/:id [get] +func (c *DetailsController) GetElementInfo(id string) { + rType := c.Ctx.Input.Params()["1"] + + var res interface{} + var err error + + switch rType { + case "data": + res, _, err = OCCatalogAPI.DataApi.DataControllerGetDataByID(context.Background(), id) + case "computing": + res, _, err = OCCatalogAPI.ComputingApi.ComputingControllerGetComputingByID(context.Background(), id) + case "datacenter": + res, _, err = OCCatalogAPI.DatacenterApi.DatacenterControllerGetOneDatacenter(context.Background(), id) + case "storage": + res, _, err = OCCatalogAPI.StorageApi.StorageControllerGet(context.Background(), id) + default: + return + } + + if err != nil { + return + } + + c.Data["data"] = res + c.TplName = "details.tpl" +} diff --git a/controllers/item.go b/controllers/item.go new file mode 100644 index 0000000..e8aed5e --- /dev/null +++ b/controllers/item.go @@ -0,0 +1,28 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// ItemController shows partners on a map +type ItemController struct { + beego.Controller +} + +// Get items +func (c *ItemController) Get() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "item.tpl" + c.EnableXSRF = false +} + +// Add item +func (c *ItemController) Add() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "item_add.tpl" + c.EnableXSRF = false +} diff --git a/controllers/login.go b/controllers/login.go new file mode 100644 index 0000000..eda6992 --- /dev/null +++ b/controllers/login.go @@ -0,0 +1,17 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// LoginController is the controller in chrage of solr +type LoginController struct { + beego.Controller +} + +// Get implements simple solr Login +func (c *LoginController) Get() { + + c.TplName = "login.tpl" + //c.SetSession("login", "test") +} diff --git a/controllers/map.go b/controllers/map.go new file mode 100644 index 0000000..ca300fc --- /dev/null +++ b/controllers/map.go @@ -0,0 +1,19 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// MapController shows partners on a map +type MapController struct { + beego.Controller +} + +// Get i +func (c *MapController) Get() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "map.tpl" + c.EnableXSRF = false +} diff --git a/controllers/networks.go b/controllers/networks.go new file mode 100644 index 0000000..6489083 --- /dev/null +++ b/controllers/networks.go @@ -0,0 +1,21 @@ +package controllers + +import ( + "fmt" + + beego "github.com/beego/beego/v2/server/web" +) + +// NetworksController is the controller in chrage of solr +type NetworksController struct { + beego.Controller +} + +// Get implements simple search +func (c *NetworksController) Get() { + resources := loadSingleFile("data/test.json") + fmt.Println(len(resources)) + fmt.Println(resources[0].Name) + c.Data["list"] = resources + c.TplName = "networks.tpl" +} diff --git a/controllers/oidc.go b/controllers/oidc.go new file mode 100644 index 0000000..2527acc --- /dev/null +++ b/controllers/oidc.go @@ -0,0 +1,96 @@ +package controllers + +import ( + "fmt" + "net/http" + + beego "github.com/beego/beego/v2/server/web" + + oidc "github.com/coreos/go-oidc" + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// OidcController is the controller in oidc +type OidcController struct { + beego.Controller +} + +var oauth2Config oauth2.Config +var idTokenVerifier *oidc.IDTokenVerifier +var ctx context.Context + +// Connect implements open id connection to openid provider +func (c *OidcController) Connect() { + ctx = context.Background() + + // Initialize a provider by specifying dex's issuer URL. + provider, err := oidc.NewProvider(ctx, "http://127.0.0.1:5556/dex") + if err != nil { + fmt.Println(err.Error()) + return + } + + // Configure the OAuth2 config with the client values. + oauth2Config = oauth2.Config{ + // client_id and client_secret of the client. + ClientID: "opencloud-search", + ClientSecret: "ZXhhbXBsZS1hcHAtc2VjcmV0", + + // The redirectURL. + RedirectURL: "http://127.0.0.1:8080/oidc-callback", + + // Discovery returns the OAuth2 endpoints. + Endpoint: provider.Endpoint(), + + // "openid" is a required scope for OpenID Connect flows. + // + // Other scopes, such as "groups" can be requested. + Scopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"}, + } + + // Create an ID token parser. + idTokenVerifier = provider.Verifier(&oidc.Config{ClientID: "opencloud-search"}) + + //state := newState() + c.Redirect(oauth2Config.AuthCodeURL("foobar"), http.StatusFound) + +} + +// Callback implements open id callback from openid provider +func (c *OidcController) Callback() { + + state := c.GetString("state") + code := c.GetString("code") + _ = state + // Verify state. + + oauth2Token, err := oauth2Config.Exchange(ctx, code) + if err != nil { + fmt.Println(err.Error()) + } + + // Extract the ID Token from OAuth2 token. + rawIDToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + // handle missing token + } + fmt.Println(rawIDToken) + // Parse and verify ID Token payload. + idToken, err := idTokenVerifier.Verify(ctx, rawIDToken) + if err != nil { + // handle error + } + + // Extract custom claims. + var claims struct { + Email string `json:"email"` + Verified bool `json:"email_verified"` + Groups []string `json:"groups"` + } + if err := idToken.Claims(&claims); err != nil { + // handle error + } + c.SetSession("login", claims.Email) + c.Redirect("/user", http.StatusFound) +} diff --git a/controllers/partner.go b/controllers/partner.go new file mode 100644 index 0000000..bdd3f7d --- /dev/null +++ b/controllers/partner.go @@ -0,0 +1,28 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// PartnerController shows partners on a map +type PartnerController struct { + beego.Controller +} + +// Get i +func (c *PartnerController) Get() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "item.tpl" + c.EnableXSRF = false +} + +// Add i +func (c *PartnerController) Add() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "map.tpl" + c.EnableXSRF = false +} diff --git a/controllers/schedule.go b/controllers/schedule.go new file mode 100644 index 0000000..10796fc --- /dev/null +++ b/controllers/schedule.go @@ -0,0 +1,15 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// ScheduleController is the controller in chrage of solr +type ScheduleController struct { + beego.Controller +} + +// Get implements simple search +func (c *ScheduleController) Get() { + c.TplName = "schedule.tpl" +} diff --git a/controllers/search.go b/controllers/search.go new file mode 100644 index 0000000..2b8a5db --- /dev/null +++ b/controllers/search.go @@ -0,0 +1,53 @@ +package controllers + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "os" + + OCCatalog_cli "oc-search/api-client/oc-catalog" + + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + + "oc-search/models" +) + +// SearchController is the controller in chrage of solr +type SearchController struct { + beego.Controller +} + +func loadSingleFile(filename string) []models.Resource { + var list []models.Resource + jsonFile, err := os.Open(filename) + // if we os.Open returns an error then handle it + if err != nil { + fmt.Println(err) + } + defer jsonFile.Close() + byteValue, _ := ioutil.ReadAll(jsonFile) + json.Unmarshal(byteValue, &list) + return list +} + +// Get implements simple search +func (c *SearchController) Get() { + query := c.GetString("q") + + var resources OCCatalog_cli.ModelsSearchResult + + //FIXME: dashboard special case for x_dash.json ????? + resources, _, err := OCCatalogAPI.SearchApi.SearchControllerSearchByWord(context.Background(), query) + if err != nil { + logs.Warn(err.Error()) + return + } + + //TODO: Adapt the View and models to use the OCCatalog_cli models as much as possible + + c.Data["list"] = resources + c.TplName = "search.tpl" +} diff --git a/controllers/user.go b/controllers/user.go new file mode 100644 index 0000000..ea66b50 --- /dev/null +++ b/controllers/user.go @@ -0,0 +1,25 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +// UserController is the controller in chrage of solr +type UserController struct { + beego.Controller +} + +// Prepare checks for logged in User +func (c *UserController) Prepare() { + login, _ := c.GetSession("login").(string) + if login == "" { + c.Ctx.Redirect(302, "/login") + } + +} + +// Get implements simple solr User +func (c *UserController) Get() { + c.Data["login"], _ = c.GetSession("login").(string) + c.TplName = "user.tpl" +} diff --git a/controllers/workflow.go b/controllers/workflow.go new file mode 100644 index 0000000..991e963 --- /dev/null +++ b/controllers/workflow.go @@ -0,0 +1,81 @@ +package controllers + +import ( + "fmt" + "log" + "net/url" + "os" + "strings" + + beego "github.com/beego/beego/v2/server/web" + "github.com/sbabiv/xml2map" +) + +// WorkflowController allows to edit a tasks workflow +type WorkflowController struct { + beego.Controller +} + +// Get i +func (c *WorkflowController) Get() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "workflow.tpl" +} + +// Open i +func (c *WorkflowController) Open() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "workflow.tpl" +} + +// Save i +func (c *WorkflowController) Save() { + xml := c.GetString("xml") + fmt.Println(xml) + decodedValue, err := url.QueryUnescape(xml) + if err != nil { + log.Fatal(err) + return + } + fmt.Println(decodedValue) + os.WriteFile("graph.xml", []byte(decodedValue), 0660) + decoder := xml2map.NewDecoder(strings.NewReader(decodedValue)) + result, err := decoder.Decode() + if err != nil { + + } + + cells := result["mxGraphModel"].(map[string]interface{})["root"].(map[string]interface{})["mxCell"].([]map[string]interface{}) + + for _, element := range cells { + id := element["@id"].(string) + if _, ok := element["@style"]; ok { + if _, ok2 := element["@rID"]; ok2 { + fmt.Print(id + ": ") + fmt.Println(element["@rID"], element["@rType"]) + } + } + if src, ok := element["@source"]; ok { + src = element["@source"].(string) + fmt.Println("Link: " + src.(string) + " " + element["@target"].(string)) + } + } + + if err != nil { + log.Fatal(err) + } + + c.StopRun() +} + +// Export i +func (c *WorkflowController) Export() { + + c.Data["Website"] = "beego.me" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "workflow.tpl" +} diff --git a/controllers/workspace.go b/controllers/workspace.go new file mode 100644 index 0000000..87b3581 --- /dev/null +++ b/controllers/workspace.go @@ -0,0 +1,90 @@ +package controllers + +import ( + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + "golang.org/x/net/context" +) + +// WorkspaceController is the controller in chrage of solr +type WorkspaceController struct { + beego.Controller +} + +// @Title GetWorkspace +// @Description Return Workspace +// @router / [get] +func (c *WorkspaceController) GetWorkspace() { + + //TODO: Use fullModel api call + workspaceObj, _, err := OCCatalogAPI.WorkspaceApi.WorkspaceControllerGetWorkspace(context.Background()) + if err != nil { + logs.Warn(err.Error()) + return + } + + outputArray := []interface{}{} + + if len(workspaceObj.Data) > 0 { + dataArr, _, err := OCCatalogAPI.DataApi.DataControllerGetMultipleDataByIDs(context.Background(), workspaceObj.Data) + if err != nil { + logs.Warn(err.Error()) + } + + //convert array of strings to interface{} + ArrInterface := make([]interface{}, len(dataArr)) + for i := range dataArr { + ArrInterface[i] = dataArr[i] + } + + outputArray = append(outputArray, ArrInterface...) + } + + if len(workspaceObj.Computing) > 0 { + computingArr, _, err := OCCatalogAPI.ComputingApi.ComputingControllerGetMultipleComputingByIDs(context.Background(), workspaceObj.Computing) + if err != nil { + logs.Warn(err.Error()) + } + + //convert array of strings to interface{} + ArrInterface := make([]interface{}, len(computingArr)) + for i := range computingArr { + ArrInterface[i] = computingArr[i] + } + + outputArray = append(outputArray, ArrInterface...) + } + + if len(workspaceObj.Datacenter) > 0 { + datacenterArr, _, err := OCCatalogAPI.DatacenterApi.DatacenterControllerGetMultipleDatacentersByIDs(context.Background(), workspaceObj.Datacenter) + if err != nil { + logs.Warn(err.Error()) + } + + //convert array of strings to interface{} + ArrInterface := make([]interface{}, len(datacenterArr)) + for i := range datacenterArr { + ArrInterface[i] = datacenterArr[i] + } + + outputArray = append(outputArray, ArrInterface...) + } + + if len(workspaceObj.Storage) > 0 { + storageArr, _, err := OCCatalogAPI.StorageApi.StorageControllerGetMultipleStoragesByIDs(context.Background(), workspaceObj.Storage) + if err != nil { + logs.Warn(err.Error()) + } + + //convert array of strings to interface{} + ArrInterface := make([]interface{}, len(storageArr)) + for i := range storageArr { + ArrInterface[i] = storageArr[i] + } + + outputArray = append(outputArray, ArrInterface...) + } + + c.Data["list"] = outputArray + c.TplName = "workspace.tpl" +} diff --git a/data/garbage/samplegraph.xml b/data/garbage/samplegraph.xml new file mode 100644 index 0000000..eebbc13 --- /dev/null +++ b/data/garbage/samplegraph.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/home.json b/data/home.json new file mode 100644 index 0000000..3cdf16c --- /dev/null +++ b/data/home.json @@ -0,0 +1,64 @@ +[{ + "id": "1", + "name": "home cluster", + "type": "computing", + "logo": "url", + "short_description": "string", + "description": "string", + "tags": ["space", "medical"], + "image": "url", + "computing": { + "cpu": 4, + "ram": 4, + "load": [], + "capabilities": [""], + "bandwith": 20 + } + }, + { + "id": "2", + "name": "Test video feed", + "type": "video", + "logo": "url", + "short_description": "my video feed", + "description": "string", + "tags": ["environment"], + "image": "url", + "video": { + "resolution": 4, + "url": 4, + "compression": [] + } + }, + { + "id": "3", + "name": "Photos", + "type": "images", + "logo": "url", + "short_description": "my images catalog", + "description": "string", + "tags": ["environment"], + "image": "url", + "images": { + "resolution": 4, + "url": 4, + "compression": [] + } + }, + { + "id": "4", + "name": "imagemagick", + "type": "processing", + "logo": "url", + "short_description": "Image conversion", + "description": "string", + "tags": ["image", "conversion"], + "image": "url", + "processing": { + "resolution": 4, + "url": 4, + "compression": [] + } + } + +] \ No newline at end of file diff --git a/data/icons/1200px-GDALLogoColor.svg.png b/data/icons/1200px-GDALLogoColor.svg.png new file mode 100644 index 0000000..6504be4 Binary files /dev/null and b/data/icons/1200px-GDALLogoColor.svg.png differ diff --git a/data/icons/959703.jpg b/data/icons/959703.jpg new file mode 100644 index 0000000..22adee3 Binary files /dev/null and b/data/icons/959703.jpg differ diff --git a/data/icons/ImageMagick_logo.svg.png b/data/icons/ImageMagick_logo.svg.png new file mode 100644 index 0000000..55acad9 Binary files /dev/null and b/data/icons/ImageMagick_logo.svg.png differ diff --git a/data/schemas/computing.json b/data/schemas/computing.json new file mode 100644 index 0000000..e69de29 diff --git a/data/schemas/data.json b/data/schemas/data.json new file mode 100644 index 0000000..e0c395a --- /dev/null +++ b/data/schemas/data.json @@ -0,0 +1,5 @@ +{ + "resource":"resource", + "description":"string", + "format":"string" +} \ No newline at end of file diff --git a/data/schemas/database.json b/data/schemas/database.json new file mode 100644 index 0000000..e69de29 diff --git a/data/schemas/fragments/ac.json b/data/schemas/fragments/ac.json new file mode 100644 index 0000000..ff75b96 --- /dev/null +++ b/data/schemas/fragments/ac.json @@ -0,0 +1,7 @@ +[ + { + "group":"string", + "allowed":"string", + "rate":"string" + } +] \ No newline at end of file diff --git a/data/schemas/fragments/address.json b/data/schemas/fragments/address.json new file mode 100644 index 0000000..f010b37 --- /dev/null +++ b/data/schemas/fragments/address.json @@ -0,0 +1,9 @@ +{ + "country":"string", + "state":"string", + "city":"string", + "postcode":"string", + "street":"string", + "street_number":"string", + "building":"string" +} \ No newline at end of file diff --git a/data/schemas/fragments/company.json b/data/schemas/fragments/company.json new file mode 100644 index 0000000..1a1aa41 --- /dev/null +++ b/data/schemas/fragments/company.json @@ -0,0 +1,4 @@ +{ + "name":"string", + "address":"address" +} \ No newline at end of file diff --git a/data/schemas/fragments/contact.json b/data/schemas/fragments/contact.json new file mode 100644 index 0000000..3dca179 --- /dev/null +++ b/data/schemas/fragments/contact.json @@ -0,0 +1,8 @@ +{ + "name":"string", + "firstname":"string", + "email":"string", + "phone":"string", + "mobile":"string", + "position":"string" +} diff --git a/data/schemas/fragments/location.json b/data/schemas/fragments/location.json new file mode 100644 index 0000000..c0ac4fe --- /dev/null +++ b/data/schemas/fragments/location.json @@ -0,0 +1,5 @@ +{ + "lat":"float", + "lon":"float", + "alt":"int" +} \ No newline at end of file diff --git a/data/schemas/fragments/processing.json b/data/schemas/fragments/processing.json new file mode 100644 index 0000000..8d7a0e6 --- /dev/null +++ b/data/schemas/fragments/processing.json @@ -0,0 +1,9 @@ +{ + "technical_details":"string", + "function":"string", + "input_format":"", + "output_format":"", + "input_media":"", + "output_media":"", + "average_processing_time":"" +} \ No newline at end of file diff --git a/data/schemas/fragments/repository.json b/data/schemas/fragments/repository.json new file mode 100644 index 0000000..864ee05 --- /dev/null +++ b/data/schemas/fragments/repository.json @@ -0,0 +1,5 @@ +{ + "name":"string", + "type":"string", + "url":"string" +} \ No newline at end of file diff --git a/data/schemas/fragments/resource.json b/data/schemas/fragments/resource.json new file mode 100644 index 0000000..ccf6cb2 --- /dev/null +++ b/data/schemas/fragments/resource.json @@ -0,0 +1,9 @@ +{ + "name" : "string", + "type" : "string", + "logo" : "url", + "short_description" : "string", + "description" : "string", + "tags" : ["space","medical"], + "image" : "url" +} \ No newline at end of file diff --git a/data/schemas/geoimage.json b/data/schemas/geoimage.json new file mode 100644 index 0000000..e69de29 diff --git a/data/schemas/image.json b/data/schemas/image.json new file mode 100644 index 0000000..e0c395a --- /dev/null +++ b/data/schemas/image.json @@ -0,0 +1,5 @@ +{ + "resource":"resource", + "description":"string", + "format":"string" +} \ No newline at end of file diff --git a/data/schemas/processing.json b/data/schemas/processing.json new file mode 100644 index 0000000..e69de29 diff --git a/data/schemas/storage.json b/data/schemas/storage.json new file mode 100644 index 0000000..e69de29 diff --git a/doc/datacenter.json b/doc/datacenter.json new file mode 100644 index 0000000..28dcdcd --- /dev/null +++ b/doc/datacenter.json @@ -0,0 +1,140 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$id": "Storage", + "type": "object", + "properties": { + "name": { + "description": "Name of the datacenter that acts as identifier", + "type": "string", + "$comment": "Maybe we should use a different tag as id" + }, + "description": { + "type": "string", + "description": "General description of the processing unit" + }, + "cpu": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "$comment": "If we use array to define multiple types of CPU (separated through motherboards?), we should use a id to be able to reffer to them" + }, + "cores": { + "type": "integer" + }, + "architecture": { + "type": "string", + "enum": [ + "amd64", + "i386", + "armv5", + "armv7", + "amr64v8", + "s390x", + "ppc64le" + ] + }, + "shared": { + "type": "boolean" + }, + "minimum_memory": { + "type": "integer", + "$comment": "I'm not sure if a CPU requires a minimum RAM to work or is just limited to the motherboard where it's installed" + }, + "platform": { + "type": "object", + "$comment": "Some CPU extensions are related to CPU platform" + } + } + } + }, + "memory": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "$comment": "Same problem as with cpu[].id" + }, + "size": { + "type": "integer", + "description": "Amount of memory in GiB" + }, + "ecc": { + "type": "boolean" + } + } + } + }, + "gpu": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "$comment": "Same problem as with cpu[].id" + }, + "memory": { + "type": "integer", + "description": "Amount of memory in GiB" + }, + "cores": { + "type": "integer", + "description": "Amount of core units" + }, + "model": { + "type": "object" + } + } + } + }, + "disk": { + "$comment": "Should a datacenter have it's own definition of storage or directly refer to storage.json# ?" + }, + "network": { + "type": "object", + "properties": { + "wan": { + "type": "array", + "$comment": "Not sure if we need expose to the user the IP address, since we are inside virtualized network/cloud", + "items": { + "type": "object", + "properties": { + "ip": { + "type": "integer", + "description": "IP address expressed in deciman notation" + }, + "ipv6": { + "type": "boolean" + }, + "bandwitch": { + "type": "integer", + "description": "Bandwith described in Mbps" + } + } + } + }, + "lan": { + "$comment": "Should be described?" + } + } + }, + "booking_price": { + "type": "object" + }, + "processing_units": { + "type": "array", + "$comment": "A datacenter object can have multiple process units associated to him", + "items": { + "$ref": "processing.json#" + } + }, + "owner": { + "type": "object" + } + } +} \ No newline at end of file diff --git a/doc/processing.json b/doc/processing.json new file mode 100644 index 0000000..c47c06e --- /dev/null +++ b/doc/processing.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$id": "Processing", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the stream that acts as identifier", + "$comment": "Must have some regex" + }, + "description": { + "type": "string", + "description": "General description of the processing unit" + }, + "repository": { + "type": "object", + "properties": { + "credentials": { + "type": "object" + }, + "url": { + "type": "string" + } + } + }, + "owner": { + "type": "object" + }, + "license": { + "type": "object" + }, + "price": { + "type": "object" + }, + "execution_requirements": { + "type": "object", + "properties": { + "parallel": { + "type": "object", + "description": "Defines if can be parallelized" + }, + "GPU": { + "type": "object" + }, + "scanling_model": { + "type": "object", + "description": "List of different configuration options" + }, + "RAM": { + "type": "object" + }, + "DISK_IO": { + "type": "object" + } + } + }, + "inputs": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "stream.json#/definitions/file" + }, + { + "$ref": "stream.json#/definitions/url" + } + ] + } + }, + "outputs": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "stream.json#/definitions/file" + }, + { + "$ref": "stream.json#/definitions/url" + } + ] + } + } + } +} \ No newline at end of file diff --git a/doc/storage.json b/doc/storage.json new file mode 100644 index 0000000..c77e339 --- /dev/null +++ b/doc/storage.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$id": "Storage", + "type": "object", + "properties": { + "name": { + "description": "Name of the stream that acts as identifier", + "type": "string", + "$comment": "Must have some regex" + }, + "description": { + "type": "string", + "description": "General description of the processing unit" + }, + "size": { + "type": "object" + }, + "encryption": { + "type": "object" + }, + "redundancy": { + "type": "object" + }, + "throughput": { + "description": "The available performance for the storage", + "type": "object" + }, + "booking_price": { + "type": "object" + }, + "type": { + "type": "object" + }, + "inputs": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "stream.json#/definitions/file" + }, + { + "$ref": "stream.json#/definitions/url" + } + ] + } + }, + "outputs": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "stream.json#/definitions/file" + }, + { + "$ref": "stream.json#/definitions/url" + } + ] + } + } + } +} \ No newline at end of file diff --git a/doc/stream.json b/doc/stream.json new file mode 100644 index 0000000..68ef672 --- /dev/null +++ b/doc/stream.json @@ -0,0 +1,86 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$id": "Stream", + "definitions": { + "file": { + "type": "object", + "description": "A stream coming from a file", + "properties": { + "description": { + "type": "string", + "description": "A user friendly description of the stream" + }, + "ftype": { + "type": "string" + }, + "example": { + "type": "string", + "description": "A example of the stream. Could be a png file, text example, etc." + }, + "location": { + "description": "System path to the file", + "type": "string" + } + } + }, + "url": { + "type": "object", + "description": "A URL pointing to some external resource", + "properties": { + "description": { + "type": "string", + "description": "A user friendly description of the resource" + }, + "protocol": { + "type": "array", + "description": "The protocol used to connect to the resource", + "$comment": "Probably just using a enum will not be enough. Some protocols could require extra stuff (like certificates, tokens, etc.)", + "items": { + "type": "string", + "enum": [ + "ftp", + "http", + "https", + "ftps", + "rstp" + ] + } + }, + "example": { + "type": "string" + }, + "location": { + "type": "string", + "description": "The endpoint itself" + } + } + } + }, + "type": "object", + "properties": { + "name": { + "description": "Name of the stream that acts as identifier", + "type": "string", + "$comment": "Must have some regex" + }, + "feature": { + "description": "A similar stream could have different features. Describing the features of the stream assures the correct results when processing it", + "type": "object" + }, + "format": { + "oneOf": [ + { + "$ref": "#/definitions/file" + }, + { + "$ref": "#/definitions/url" + } + ] + } + }, + "required": [ + "name", + "feature", + "format" + ] +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0e08440 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.4' + +services: + oc-search: + build: . + container_name: oc-search + ports: + - 8080:8080 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..976b92d --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module oc-search + +go 1.15 + +require ( + github.com/antihax/optional v1.0.0 + github.com/beego/beego/v2 v2.0.1 + github.com/coreos/go-oidc v2.2.1+incompatible + github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect + github.com/sbabiv/xml2map v1.2.0 + github.com/smartystreets/goconvey v1.6.4 + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 + golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3 + golang.org/x/sys v0.0.0-20210314195730-07df6a141424 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2ba5eac --- /dev/null +++ b/go.sum @@ -0,0 +1,557 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE= +github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI= +github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= +github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= +github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= +github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= +github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M= +github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= +github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sbabiv/xml2map v1.2.0 h1:US5Yuor+DCBQHqikReNNSEYSZChG4A/IXDHVski1rt8= +github.com/sbabiv/xml2map v1.2.0/go.mod h1:lfLWTANdsSP8X8CqGtDcBA8sHOcdmd0RiN7QTb3Ee4g= +github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo= +github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= +github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= +go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3 h1:BaN3BAqnopnKjvl+15DYP6LLrbBHfbfmlFYzmFj/Q9Q= +golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210314195730-07df6a141424 h1:+39ahH47SWi1PhMRAHfIrm8f69HRZ5K2koXH6dmO8TQ= +golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag= +golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/graph.xml b/graph.xml new file mode 100644 index 0000000..9ff1818 --- /dev/null +++ b/graph.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lastupdate.tmp b/lastupdate.tmp new file mode 100755 index 0000000..34def63 --- /dev/null +++ b/lastupdate.tmp @@ -0,0 +1 @@ +{"/home/yves/Documents/code/go/opencloud/oc-search/controllers":1693079638537542953} \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..5eb9a58 --- /dev/null +++ b/main.go @@ -0,0 +1,17 @@ +package main + +import ( + _ "oc-search/routers" + _ "oc-search/views" + + beego "github.com/beego/beego/v2/server/web" +) + +func main() { + beego.BConfig.WebConfig.Session.SessionOn = true + beego.SetStaticPath("/favicon.ico", "/static/favicon.ico") + beego.BConfig.WebConfig.DirectoryIndex = true + beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" + + beego.Run() +} diff --git a/menu.html b/menu.html new file mode 100644 index 0000000..f5696fa --- /dev/null +++ b/menu.html @@ -0,0 +1,112 @@ + + + + + + o-search + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/models/access.go b/models/access.go new file mode 100644 index 0000000..4761d20 --- /dev/null +++ b/models/access.go @@ -0,0 +1,7 @@ +package models + +type Access struct { + Name string `json:"name"` + Feature string `json:"feature"` + Rights string `json:"rights"` +} diff --git a/models/coordinates.go b/models/coordinates.go new file mode 100644 index 0000000..a8031ff --- /dev/null +++ b/models/coordinates.go @@ -0,0 +1,7 @@ +package models + +// Coordinates represents WGS84 geographical coordiantes +type Coordinates struct { + Latitude float32 `json:"latitude"` + Longitude float32 `json:"longitude"` +} diff --git a/models/data.go b/models/data.go new file mode 100644 index 0000000..8fb010c --- /dev/null +++ b/models/data.go @@ -0,0 +1,23 @@ +package models + +type Data struct { + Name string `json:"name"` + Type string `json:"type"` + Logo string `json:"logo"` + ShortDescription string `json:"short_description"` + Description string `json:"description"` + Tags []string `json:"tags"` + Image string `json:"image"` + Id string `json:"id"` + SourceId string `json:"source_id"` + SourceProvider string `json:"source_provider"` + SourceUrl string `json:"source_url"` + Format string `json:"format"` + Size int `json:"size"` + License string `json:"license"` + DataUrl string `json:"url"` + Price string `json:"price"` + CreationDate string `json:"creation_date"` + ModificationDate string `json:"modification_date"` + Position Coordinates `json:"position"` +} diff --git a/models/graph.go b/models/graph.go new file mode 100644 index 0000000..7b10eca --- /dev/null +++ b/models/graph.go @@ -0,0 +1,15 @@ +package models + +type GraphElement struct { + Id int `json:"id"` + Name string `json:"name"` + Type string `json:"type"` +} + +type GraphLink struct { + Id int `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Source int `json:"source"` + Destination int `json:"destination"` +} diff --git a/models/processing.go b/models/processing.go new file mode 100644 index 0000000..69290b1 --- /dev/null +++ b/models/processing.go @@ -0,0 +1,22 @@ +package models + +type Processing struct { + Name string `json:"name"` + Type string `json:"type"` + Logo string `json:"logo"` + ShortDescription string `json:"short_description"` + Description string `json:"description"` + Tags []string `json:"tags"` + Image string `json:"image"` + Id string `json:"id"` + SourceId string `json:"source_id"` + SourceProvider string `json:"source_provider"` + SourceUrl string `json:"source_url"` + Format string `json:"format"` + Size int `json:"size"` + License string `json:"license"` + DataUrl string `json:"url"` + Price string `json:"price"` + CreationDate string `json:"creation_date"` + ModificationDate string `json:"modification_date"` +} diff --git a/models/profile.go b/models/profile.go new file mode 100644 index 0000000..e37cbb7 --- /dev/null +++ b/models/profile.go @@ -0,0 +1,12 @@ +package models + +type Profile struct { + Name string `json:"name"` + Type string `json:"type"` + Logo string `json:"logo"` + ShortDescription string `json:"short_description"` + Description string `json:"description"` + Tags []string `json:"tags"` + Image string `json:"image"` + Id string `json:"id"` +} diff --git a/models/resource.go b/models/resource.go new file mode 100644 index 0000000..f565066 --- /dev/null +++ b/models/resource.go @@ -0,0 +1,12 @@ +package models + +type Resource struct { + Name string `json:"name"` + Type string `json:"type"` + Logo string `json:"logo"` + ShortDescription string `json:"short_description"` + Description string `json:"description"` + Tags []string `json:"tags"` + Image string `json:"image"` + Id string `json:"id"` +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..88757fe --- /dev/null +++ b/models/user.go @@ -0,0 +1,9 @@ +package models + +type User struct { + Name string `json:"name"` + Email string `json:"email"` + Profile string `json:"profiles"` + Image string `json:"image"` + Id string `json:"id"` +} diff --git a/models/workflow/executionschedule.go b/models/workflow/executionschedule.go new file mode 100644 index 0000000..f433549 --- /dev/null +++ b/models/workflow/executionschedule.go @@ -0,0 +1,11 @@ +package models + +import "time" + +// ExecutionSchedule an execution unit or a set of steps +type ExecutionSchedule struct { + Name string `json:"name"` + Start time.Time `json:"start"` + Stop time.Time `json:"stop"` + Reccurence string `json:"reccurence"` +} diff --git a/models/workflow/workflow.go b/models/workflow/workflow.go new file mode 100644 index 0000000..8a3e55f --- /dev/null +++ b/models/workflow/workflow.go @@ -0,0 +1,8 @@ +package models + +// Coordinates represents WGS84 geographical coordiantes +type Workflow struct { + Name string `json:"name"` + PlannedSchedule ExecutionSchedule `json:"plannedschedule` + Steps []WorkflowStep `json:"steps"` +} diff --git a/models/workflow/workflowstep.go b/models/workflow/workflowstep.go new file mode 100644 index 0000000..78e217f --- /dev/null +++ b/models/workflow/workflowstep.go @@ -0,0 +1,10 @@ +package models + +// WorkflowStep an execution unit or a set of steps +type WorkflowStep struct { + Name string `json:"name"` + Container string `json:"container"` + Input string `json:"input"` + Output string `json:"output"` + Location string `json:"location"` +} diff --git a/oc-search b/oc-search new file mode 100755 index 0000000..865f710 Binary files /dev/null and b/oc-search differ diff --git a/routers/commentsRouter_____________home_valentin_kivachuk_InternalProjects_oc-search_controllers.go b/routers/commentsRouter_____________home_valentin_kivachuk_InternalProjects_oc-search_controllers.go new file mode 100644 index 0000000..7766f89 --- /dev/null +++ b/routers/commentsRouter_____________home_valentin_kivachuk_InternalProjects_oc-search_controllers.go @@ -0,0 +1,107 @@ +package routers + +import ( + beego "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context/param" +) + +func init() { + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/computing/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/computing/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/data/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/data/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/datacenter/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/datacenter/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/storage/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/storage/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:WorkspaceController"] = append(beego.GlobalControllerRouter["oc-search/controllers:WorkspaceController"], + beego.ControllerComments{ + Method: "GetWorkspace", + Router: "/", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go new file mode 100644 index 0000000..7766f89 --- /dev/null +++ b/routers/commentsRouter_controllers.go @@ -0,0 +1,107 @@ +package routers + +import ( + beego "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context/param" +) + +func init() { + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/computing/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/computing/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/data/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/data/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/datacenter/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/datacenter/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "SubmitToWorkspace", + Router: "/storage/:id", + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:DetailsController"] = append(beego.GlobalControllerRouter["oc-search/controllers:DetailsController"], + beego.ControllerComments{ + Method: "GetElementInfo", + Router: "/storage/:id", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make( + param.New("id", param.InPath), + ), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["oc-search/controllers:WorkspaceController"] = append(beego.GlobalControllerRouter["oc-search/controllers:WorkspaceController"], + beego.ControllerComments{ + Method: "GetWorkspace", + Router: "/", + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + +} diff --git a/routers/router.go b/routers/router.go new file mode 100644 index 0000000..d5613fe --- /dev/null +++ b/routers/router.go @@ -0,0 +1,52 @@ +package routers + +import ( + "oc-search/controllers" + "os" + + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" +) + +func init() { + + // Remove old swagger comments + err := os.Remove("routers/commentsRouter_controllers.go") + if err != nil { + logs.Warning("Couldn't remove comments file: " + err.Error()) + } + + beego.Router("/", &controllers.MainController{}) + beego.Router("/search", &controllers.SearchController{}) + + beego.Router("/login", &controllers.LoginController{}) + beego.Router("/oidc", &controllers.OidcController{}, "get:Connect") + beego.Router("/oidc-callback", &controllers.OidcController{}, "*:Callback") + beego.Router("/user", &controllers.UserController{}) + beego.Router("/workflow", &controllers.WorkflowController{}) + beego.Router("/workflow/open", &controllers.WorkflowController{}, "*:Open") + beego.Router("/workflow/save", &controllers.WorkflowController{}, "*:Save") + beego.Router("/workflow/export", &controllers.WorkflowController{}, "*:Export") + beego.Router("/schedule", &controllers.ScheduleController{}) + beego.Router("/map", &controllers.MapController{}) + beego.Router("/datacenter", &controllers.DatacenterController{}) + + // beego.Router("/workspace", &controllers.WorkspaceController{}) + + // Details of the resources + beego.AddNamespace( + beego.NewNamespace( + "/details", beego.NSInclude(&controllers.DetailsController{}), + ), + beego.NewNamespace( + "/workspace", beego.NSInclude(&controllers.WorkspaceController{}), + ), + ) + // beego.Router("/details/?:id", &controllers.DetailsController{}) + + // Feed data + beego.Router("/partner", &controllers.PartnerController{}) + beego.Router("/item", &controllers.ItemController{}) + beego.Router("/item/add", &controllers.ItemController{}, "*:Add") + +} diff --git a/scripts/generate_client.sh b/scripts/generate_client.sh new file mode 100755 index 0000000..fb344ff --- /dev/null +++ b/scripts/generate_client.sh @@ -0,0 +1,15 @@ +#!/bin/env bash +set -xe # Don't continue if first command fail + +docker run \ + --rm \ + --user=$UID \ + --network=host \ + -v ${PWD}:/local \ + swaggerapi/swagger-codegen-cli:2.4.18 \ + generate -i http://localhost:49618/swagger/swagger.json -l go -o /local/api-client/oc-catalog.bak + +if [ -d api-client/oc-catalog.bak ]; then + rm -r api-client/oc-catalog + mv api-client/oc-catalog.bak api-client/oc-catalog +fi diff --git a/static/css/bootstrap.min.css b/static/css/bootstrap.min.css new file mode 100644 index 0000000..33edb05 --- /dev/null +++ b/static/css/bootstrap.min.css @@ -0,0 +1,7 @@ +@charset "UTF-8";/*! + * Bootstrap v5.0.0-beta2 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x)/ -2);margin-left:calc(var(--bs-gutter-x)/ -2)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x)/ 2);padding-left:calc(var(--bs-gutter-x)/ 2);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.3333333333%}.col-2{flex:0 0 auto;width:16.6666666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.3333333333%}.col-5{flex:0 0 auto;width:41.6666666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.3333333333%}.col-8{flex:0 0 auto;width:66.6666666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.3333333333%}.col-11{flex:0 0 auto;width:91.6666666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.3333333333%}.col-sm-2{flex:0 0 auto;width:16.6666666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.3333333333%}.col-sm-5{flex:0 0 auto;width:41.6666666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.3333333333%}.col-sm-8{flex:0 0 auto;width:66.6666666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.3333333333%}.col-sm-11{flex:0 0 auto;width:91.6666666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.3333333333%}.col-md-2{flex:0 0 auto;width:16.6666666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.3333333333%}.col-md-5{flex:0 0 auto;width:41.6666666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.3333333333%}.col-md-8{flex:0 0 auto;width:66.6666666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.3333333333%}.col-md-11{flex:0 0 auto;width:91.6666666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.3333333333%}.col-lg-2{flex:0 0 auto;width:16.6666666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.3333333333%}.col-lg-5{flex:0 0 auto;width:41.6666666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.3333333333%}.col-lg-8{flex:0 0 auto;width:66.6666666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.3333333333%}.col-lg-11{flex:0 0 auto;width:91.6666666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.3333333333%}.col-xl-2{flex:0 0 auto;width:16.6666666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.3333333333%}.col-xl-5{flex:0 0 auto;width:41.6666666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.3333333333%}.col-xl-8{flex:0 0 auto;width:66.6666666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.3333333333%}.col-xl-11{flex:0 0 auto;width:91.6666666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.3333333333%}.col-xxl-2{flex:0 0 auto;width:16.6666666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.3333333333%}.col-xxl-5{flex:0 0 auto;width:41.6666666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.3333333333%}.col-xxl-8{flex:0 0 auto;width:66.6666666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.3333333333%}.col-xxl-11{flex:0 0 auto;width:91.6666666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.3333333333%}.offset-xxl-2{margin-left:16.6666666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.3333333333%}.offset-xxl-5{margin-left:41.6666666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.3333333333%}.offset-xxl-8{margin-left:66.6666666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.3333333333%}.offset-xxl-11{margin-left:91.6666666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{color:#6c757d;background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);padding:1rem .75rem}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control::-webkit-input-placeholder{color:transparent}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754;padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545;padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%}.dropup .dropdown-menu[data-bs-popper]{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu{top:0;right:auto;left:100%}.dropend .dropdown-menu[data-bs-popper]{margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu{top:0;right:100%;left:auto}.dropstart .dropdown-menu[data-bs-popper]{margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:transparent;border:1px solid rgba(0,0,0,.125);border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button.collapsed{border-bottom-width:0}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item:first-of-type .accordion-button{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-width:1px;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-collapse{border-bottom-width:1px;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-collapse{border:solid rgba(0,0,0,.125);border-width:0 1px}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-button{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item:first-of-type .accordion-button{border-top-width:0;border-top-left-radius:0;border-top-right-radius:0}.accordion-flush .accordion-item:last-of-type .accordion-button.collapsed{border-bottom-width:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{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);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid #d8d8d8;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-0{border-width:0!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/static/css/leaflet.css b/static/css/leaflet.css new file mode 100644 index 0000000..983d605 --- /dev/null +++ b/static/css/leaflet.css @@ -0,0 +1,640 @@ +/* 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; + } +/* Prevents IE11 from highlighting tiles in blue */ +.leaflet-tile::selection { + background: transparent; +} +/* 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, +.leaflet-container .leaflet-tile { + 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; + 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); + 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; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: 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; + cursor: 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, +svg.leaflet-image-layer.leaflet-interactive path { + 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); + 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; + } diff --git a/static/css/material-icons.css b/static/css/material-icons.css new file mode 100644 index 0000000..48acbf9 --- /dev/null +++ b/static/css/material-icons.css @@ -0,0 +1,23 @@ +/* fallback */ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(material-icons.woff2) format('woff2'); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -moz-font-feature-settings: 'liga'; + -moz-osx-font-smoothing: grayscale; +} diff --git a/static/css/material-icons.woff2 b/static/css/material-icons.woff2 new file mode 100644 index 0000000..2b86ebf Binary files /dev/null and b/static/css/material-icons.woff2 differ diff --git a/static/css/materialize.min.css b/static/css/materialize.min.css new file mode 100644 index 0000000..74b1741 --- /dev/null +++ b/static/css/materialize.min.css @@ -0,0 +1,13 @@ +/*! + * Materialize v1.0.0 (http://materializecss.com) + * Copyright 2014-2017 Materialize + * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE) + */ +.materialize-red{background-color:#e51c23 !important}.materialize-red-text{color:#e51c23 !important}.materialize-red.lighten-5{background-color:#fdeaeb !important}.materialize-red-text.text-lighten-5{color:#fdeaeb !important}.materialize-red.lighten-4{background-color:#f8c1c3 !important}.materialize-red-text.text-lighten-4{color:#f8c1c3 !important}.materialize-red.lighten-3{background-color:#f3989b !important}.materialize-red-text.text-lighten-3{color:#f3989b !important}.materialize-red.lighten-2{background-color:#ee6e73 !important}.materialize-red-text.text-lighten-2{color:#ee6e73 !important}.materialize-red.lighten-1{background-color:#ea454b !important}.materialize-red-text.text-lighten-1{color:#ea454b !important}.materialize-red.darken-1{background-color:#d0181e !important}.materialize-red-text.text-darken-1{color:#d0181e !important}.materialize-red.darken-2{background-color:#b9151b !important}.materialize-red-text.text-darken-2{color:#b9151b !important}.materialize-red.darken-3{background-color:#a21318 !important}.materialize-red-text.text-darken-3{color:#a21318 !important}.materialize-red.darken-4{background-color:#8b1014 !important}.materialize-red-text.text-darken-4{color:#8b1014 !important}.red{background-color:#F44336 !important}.red-text{color:#F44336 !important}.red.lighten-5{background-color:#FFEBEE !important}.red-text.text-lighten-5{color:#FFEBEE !important}.red.lighten-4{background-color:#FFCDD2 !important}.red-text.text-lighten-4{color:#FFCDD2 !important}.red.lighten-3{background-color:#EF9A9A !important}.red-text.text-lighten-3{color:#EF9A9A !important}.red.lighten-2{background-color:#E57373 !important}.red-text.text-lighten-2{color:#E57373 !important}.red.lighten-1{background-color:#EF5350 !important}.red-text.text-lighten-1{color:#EF5350 !important}.red.darken-1{background-color:#E53935 !important}.red-text.text-darken-1{color:#E53935 !important}.red.darken-2{background-color:#D32F2F !important}.red-text.text-darken-2{color:#D32F2F !important}.red.darken-3{background-color:#C62828 !important}.red-text.text-darken-3{color:#C62828 !important}.red.darken-4{background-color:#B71C1C !important}.red-text.text-darken-4{color:#B71C1C !important}.red.accent-1{background-color:#FF8A80 !important}.red-text.text-accent-1{color:#FF8A80 !important}.red.accent-2{background-color:#FF5252 !important}.red-text.text-accent-2{color:#FF5252 !important}.red.accent-3{background-color:#FF1744 !important}.red-text.text-accent-3{color:#FF1744 !important}.red.accent-4{background-color:#D50000 !important}.red-text.text-accent-4{color:#D50000 !important}.pink{background-color:#e91e63 !important}.pink-text{color:#e91e63 !important}.pink.lighten-5{background-color:#fce4ec !important}.pink-text.text-lighten-5{color:#fce4ec !important}.pink.lighten-4{background-color:#f8bbd0 !important}.pink-text.text-lighten-4{color:#f8bbd0 !important}.pink.lighten-3{background-color:#f48fb1 !important}.pink-text.text-lighten-3{color:#f48fb1 !important}.pink.lighten-2{background-color:#f06292 !important}.pink-text.text-lighten-2{color:#f06292 !important}.pink.lighten-1{background-color:#ec407a !important}.pink-text.text-lighten-1{color:#ec407a !important}.pink.darken-1{background-color:#d81b60 !important}.pink-text.text-darken-1{color:#d81b60 !important}.pink.darken-2{background-color:#c2185b !important}.pink-text.text-darken-2{color:#c2185b !important}.pink.darken-3{background-color:#ad1457 !important}.pink-text.text-darken-3{color:#ad1457 !important}.pink.darken-4{background-color:#880e4f !important}.pink-text.text-darken-4{color:#880e4f !important}.pink.accent-1{background-color:#ff80ab !important}.pink-text.text-accent-1{color:#ff80ab !important}.pink.accent-2{background-color:#ff4081 !important}.pink-text.text-accent-2{color:#ff4081 !important}.pink.accent-3{background-color:#f50057 !important}.pink-text.text-accent-3{color:#f50057 !important}.pink.accent-4{background-color:#c51162 !important}.pink-text.text-accent-4{color:#c51162 !important}.purple{background-color:#9c27b0 !important}.purple-text{color:#9c27b0 !important}.purple.lighten-5{background-color:#f3e5f5 !important}.purple-text.text-lighten-5{color:#f3e5f5 !important}.purple.lighten-4{background-color:#e1bee7 !important}.purple-text.text-lighten-4{color:#e1bee7 !important}.purple.lighten-3{background-color:#ce93d8 !important}.purple-text.text-lighten-3{color:#ce93d8 !important}.purple.lighten-2{background-color:#ba68c8 !important}.purple-text.text-lighten-2{color:#ba68c8 !important}.purple.lighten-1{background-color:#ab47bc !important}.purple-text.text-lighten-1{color:#ab47bc !important}.purple.darken-1{background-color:#8e24aa !important}.purple-text.text-darken-1{color:#8e24aa !important}.purple.darken-2{background-color:#7b1fa2 !important}.purple-text.text-darken-2{color:#7b1fa2 !important}.purple.darken-3{background-color:#6a1b9a !important}.purple-text.text-darken-3{color:#6a1b9a !important}.purple.darken-4{background-color:#4a148c !important}.purple-text.text-darken-4{color:#4a148c !important}.purple.accent-1{background-color:#ea80fc !important}.purple-text.text-accent-1{color:#ea80fc !important}.purple.accent-2{background-color:#e040fb !important}.purple-text.text-accent-2{color:#e040fb !important}.purple.accent-3{background-color:#d500f9 !important}.purple-text.text-accent-3{color:#d500f9 !important}.purple.accent-4{background-color:#a0f !important}.purple-text.text-accent-4{color:#a0f !important}.deep-purple{background-color:#673ab7 !important}.deep-purple-text{color:#673ab7 !important}.deep-purple.lighten-5{background-color:#ede7f6 !important}.deep-purple-text.text-lighten-5{color:#ede7f6 !important}.deep-purple.lighten-4{background-color:#d1c4e9 !important}.deep-purple-text.text-lighten-4{color:#d1c4e9 !important}.deep-purple.lighten-3{background-color:#b39ddb !important}.deep-purple-text.text-lighten-3{color:#b39ddb !important}.deep-purple.lighten-2{background-color:#9575cd !important}.deep-purple-text.text-lighten-2{color:#9575cd !important}.deep-purple.lighten-1{background-color:#7e57c2 !important}.deep-purple-text.text-lighten-1{color:#7e57c2 !important}.deep-purple.darken-1{background-color:#5e35b1 !important}.deep-purple-text.text-darken-1{color:#5e35b1 !important}.deep-purple.darken-2{background-color:#512da8 !important}.deep-purple-text.text-darken-2{color:#512da8 !important}.deep-purple.darken-3{background-color:#4527a0 !important}.deep-purple-text.text-darken-3{color:#4527a0 !important}.deep-purple.darken-4{background-color:#311b92 !important}.deep-purple-text.text-darken-4{color:#311b92 !important}.deep-purple.accent-1{background-color:#b388ff !important}.deep-purple-text.text-accent-1{color:#b388ff !important}.deep-purple.accent-2{background-color:#7c4dff !important}.deep-purple-text.text-accent-2{color:#7c4dff !important}.deep-purple.accent-3{background-color:#651fff !important}.deep-purple-text.text-accent-3{color:#651fff !important}.deep-purple.accent-4{background-color:#6200ea !important}.deep-purple-text.text-accent-4{color:#6200ea !important}.indigo{background-color:#3f51b5 !important}.indigo-text{color:#3f51b5 !important}.indigo.lighten-5{background-color:#e8eaf6 !important}.indigo-text.text-lighten-5{color:#e8eaf6 !important}.indigo.lighten-4{background-color:#c5cae9 !important}.indigo-text.text-lighten-4{color:#c5cae9 !important}.indigo.lighten-3{background-color:#9fa8da !important}.indigo-text.text-lighten-3{color:#9fa8da !important}.indigo.lighten-2{background-color:#7986cb !important}.indigo-text.text-lighten-2{color:#7986cb !important}.indigo.lighten-1{background-color:#5c6bc0 !important}.indigo-text.text-lighten-1{color:#5c6bc0 !important}.indigo.darken-1{background-color:#3949ab !important}.indigo-text.text-darken-1{color:#3949ab !important}.indigo.darken-2{background-color:#303f9f !important}.indigo-text.text-darken-2{color:#303f9f !important}.indigo.darken-3{background-color:#283593 !important}.indigo-text.text-darken-3{color:#283593 !important}.indigo.darken-4{background-color:#1a237e !important}.indigo-text.text-darken-4{color:#1a237e !important}.indigo.accent-1{background-color:#8c9eff !important}.indigo-text.text-accent-1{color:#8c9eff !important}.indigo.accent-2{background-color:#536dfe !important}.indigo-text.text-accent-2{color:#536dfe !important}.indigo.accent-3{background-color:#3d5afe !important}.indigo-text.text-accent-3{color:#3d5afe !important}.indigo.accent-4{background-color:#304ffe !important}.indigo-text.text-accent-4{color:#304ffe !important}.blue{background-color:#2196F3 !important}.blue-text{color:#2196F3 !important}.blue.lighten-5{background-color:#E3F2FD !important}.blue-text.text-lighten-5{color:#E3F2FD !important}.blue.lighten-4{background-color:#BBDEFB !important}.blue-text.text-lighten-4{color:#BBDEFB !important}.blue.lighten-3{background-color:#90CAF9 !important}.blue-text.text-lighten-3{color:#90CAF9 !important}.blue.lighten-2{background-color:#64B5F6 !important}.blue-text.text-lighten-2{color:#64B5F6 !important}.blue.lighten-1{background-color:#42A5F5 !important}.blue-text.text-lighten-1{color:#42A5F5 !important}.blue.darken-1{background-color:#1E88E5 !important}.blue-text.text-darken-1{color:#1E88E5 !important}.blue.darken-2{background-color:#1976D2 !important}.blue-text.text-darken-2{color:#1976D2 !important}.blue.darken-3{background-color:#1565C0 !important}.blue-text.text-darken-3{color:#1565C0 !important}.blue.darken-4{background-color:#0D47A1 !important}.blue-text.text-darken-4{color:#0D47A1 !important}.blue.accent-1{background-color:#82B1FF !important}.blue-text.text-accent-1{color:#82B1FF !important}.blue.accent-2{background-color:#448AFF !important}.blue-text.text-accent-2{color:#448AFF !important}.blue.accent-3{background-color:#2979FF !important}.blue-text.text-accent-3{color:#2979FF !important}.blue.accent-4{background-color:#2962FF !important}.blue-text.text-accent-4{color:#2962FF !important}.light-blue{background-color:#03a9f4 !important}.light-blue-text{color:#03a9f4 !important}.light-blue.lighten-5{background-color:#e1f5fe !important}.light-blue-text.text-lighten-5{color:#e1f5fe !important}.light-blue.lighten-4{background-color:#b3e5fc !important}.light-blue-text.text-lighten-4{color:#b3e5fc !important}.light-blue.lighten-3{background-color:#81d4fa !important}.light-blue-text.text-lighten-3{color:#81d4fa !important}.light-blue.lighten-2{background-color:#4fc3f7 !important}.light-blue-text.text-lighten-2{color:#4fc3f7 !important}.light-blue.lighten-1{background-color:#29b6f6 !important}.light-blue-text.text-lighten-1{color:#29b6f6 !important}.light-blue.darken-1{background-color:#039be5 !important}.light-blue-text.text-darken-1{color:#039be5 !important}.light-blue.darken-2{background-color:#0288d1 !important}.light-blue-text.text-darken-2{color:#0288d1 !important}.light-blue.darken-3{background-color:#0277bd !important}.light-blue-text.text-darken-3{color:#0277bd !important}.light-blue.darken-4{background-color:#01579b !important}.light-blue-text.text-darken-4{color:#01579b !important}.light-blue.accent-1{background-color:#80d8ff !important}.light-blue-text.text-accent-1{color:#80d8ff !important}.light-blue.accent-2{background-color:#40c4ff !important}.light-blue-text.text-accent-2{color:#40c4ff !important}.light-blue.accent-3{background-color:#00b0ff !important}.light-blue-text.text-accent-3{color:#00b0ff !important}.light-blue.accent-4{background-color:#0091ea !important}.light-blue-text.text-accent-4{color:#0091ea !important}.cyan{background-color:#00bcd4 !important}.cyan-text{color:#00bcd4 !important}.cyan.lighten-5{background-color:#e0f7fa !important}.cyan-text.text-lighten-5{color:#e0f7fa !important}.cyan.lighten-4{background-color:#b2ebf2 !important}.cyan-text.text-lighten-4{color:#b2ebf2 !important}.cyan.lighten-3{background-color:#80deea !important}.cyan-text.text-lighten-3{color:#80deea !important}.cyan.lighten-2{background-color:#4dd0e1 !important}.cyan-text.text-lighten-2{color:#4dd0e1 !important}.cyan.lighten-1{background-color:#26c6da !important}.cyan-text.text-lighten-1{color:#26c6da !important}.cyan.darken-1{background-color:#00acc1 !important}.cyan-text.text-darken-1{color:#00acc1 !important}.cyan.darken-2{background-color:#0097a7 !important}.cyan-text.text-darken-2{color:#0097a7 !important}.cyan.darken-3{background-color:#00838f !important}.cyan-text.text-darken-3{color:#00838f !important}.cyan.darken-4{background-color:#006064 !important}.cyan-text.text-darken-4{color:#006064 !important}.cyan.accent-1{background-color:#84ffff !important}.cyan-text.text-accent-1{color:#84ffff !important}.cyan.accent-2{background-color:#18ffff !important}.cyan-text.text-accent-2{color:#18ffff !important}.cyan.accent-3{background-color:#00e5ff !important}.cyan-text.text-accent-3{color:#00e5ff !important}.cyan.accent-4{background-color:#00b8d4 !important}.cyan-text.text-accent-4{color:#00b8d4 !important}.teal{background-color:#009688 !important}.teal-text{color:#009688 !important}.teal.lighten-5{background-color:#e0f2f1 !important}.teal-text.text-lighten-5{color:#e0f2f1 !important}.teal.lighten-4{background-color:#b2dfdb !important}.teal-text.text-lighten-4{color:#b2dfdb !important}.teal.lighten-3{background-color:#80cbc4 !important}.teal-text.text-lighten-3{color:#80cbc4 !important}.teal.lighten-2{background-color:#4db6ac !important}.teal-text.text-lighten-2{color:#4db6ac !important}.teal.lighten-1{background-color:#26a69a !important}.teal-text.text-lighten-1{color:#26a69a !important}.teal.darken-1{background-color:#00897b !important}.teal-text.text-darken-1{color:#00897b !important}.teal.darken-2{background-color:#00796b !important}.teal-text.text-darken-2{color:#00796b !important}.teal.darken-3{background-color:#00695c !important}.teal-text.text-darken-3{color:#00695c !important}.teal.darken-4{background-color:#004d40 !important}.teal-text.text-darken-4{color:#004d40 !important}.teal.accent-1{background-color:#a7ffeb !important}.teal-text.text-accent-1{color:#a7ffeb !important}.teal.accent-2{background-color:#64ffda !important}.teal-text.text-accent-2{color:#64ffda !important}.teal.accent-3{background-color:#1de9b6 !important}.teal-text.text-accent-3{color:#1de9b6 !important}.teal.accent-4{background-color:#00bfa5 !important}.teal-text.text-accent-4{color:#00bfa5 !important}.green{background-color:#4CAF50 !important}.green-text{color:#4CAF50 !important}.green.lighten-5{background-color:#E8F5E9 !important}.green-text.text-lighten-5{color:#E8F5E9 !important}.green.lighten-4{background-color:#C8E6C9 !important}.green-text.text-lighten-4{color:#C8E6C9 !important}.green.lighten-3{background-color:#A5D6A7 !important}.green-text.text-lighten-3{color:#A5D6A7 !important}.green.lighten-2{background-color:#81C784 !important}.green-text.text-lighten-2{color:#81C784 !important}.green.lighten-1{background-color:#66BB6A !important}.green-text.text-lighten-1{color:#66BB6A !important}.green.darken-1{background-color:#43A047 !important}.green-text.text-darken-1{color:#43A047 !important}.green.darken-2{background-color:#388E3C !important}.green-text.text-darken-2{color:#388E3C !important}.green.darken-3{background-color:#2E7D32 !important}.green-text.text-darken-3{color:#2E7D32 !important}.green.darken-4{background-color:#1B5E20 !important}.green-text.text-darken-4{color:#1B5E20 !important}.green.accent-1{background-color:#B9F6CA !important}.green-text.text-accent-1{color:#B9F6CA !important}.green.accent-2{background-color:#69F0AE !important}.green-text.text-accent-2{color:#69F0AE !important}.green.accent-3{background-color:#00E676 !important}.green-text.text-accent-3{color:#00E676 !important}.green.accent-4{background-color:#00C853 !important}.green-text.text-accent-4{color:#00C853 !important}.light-green{background-color:#8bc34a !important}.light-green-text{color:#8bc34a !important}.light-green.lighten-5{background-color:#f1f8e9 !important}.light-green-text.text-lighten-5{color:#f1f8e9 !important}.light-green.lighten-4{background-color:#dcedc8 !important}.light-green-text.text-lighten-4{color:#dcedc8 !important}.light-green.lighten-3{background-color:#c5e1a5 !important}.light-green-text.text-lighten-3{color:#c5e1a5 !important}.light-green.lighten-2{background-color:#aed581 !important}.light-green-text.text-lighten-2{color:#aed581 !important}.light-green.lighten-1{background-color:#9ccc65 !important}.light-green-text.text-lighten-1{color:#9ccc65 !important}.light-green.darken-1{background-color:#7cb342 !important}.light-green-text.text-darken-1{color:#7cb342 !important}.light-green.darken-2{background-color:#689f38 !important}.light-green-text.text-darken-2{color:#689f38 !important}.light-green.darken-3{background-color:#558b2f !important}.light-green-text.text-darken-3{color:#558b2f !important}.light-green.darken-4{background-color:#33691e !important}.light-green-text.text-darken-4{color:#33691e !important}.light-green.accent-1{background-color:#ccff90 !important}.light-green-text.text-accent-1{color:#ccff90 !important}.light-green.accent-2{background-color:#b2ff59 !important}.light-green-text.text-accent-2{color:#b2ff59 !important}.light-green.accent-3{background-color:#76ff03 !important}.light-green-text.text-accent-3{color:#76ff03 !important}.light-green.accent-4{background-color:#64dd17 !important}.light-green-text.text-accent-4{color:#64dd17 !important}.lime{background-color:#cddc39 !important}.lime-text{color:#cddc39 !important}.lime.lighten-5{background-color:#f9fbe7 !important}.lime-text.text-lighten-5{color:#f9fbe7 !important}.lime.lighten-4{background-color:#f0f4c3 !important}.lime-text.text-lighten-4{color:#f0f4c3 !important}.lime.lighten-3{background-color:#e6ee9c !important}.lime-text.text-lighten-3{color:#e6ee9c !important}.lime.lighten-2{background-color:#dce775 !important}.lime-text.text-lighten-2{color:#dce775 !important}.lime.lighten-1{background-color:#d4e157 !important}.lime-text.text-lighten-1{color:#d4e157 !important}.lime.darken-1{background-color:#c0ca33 !important}.lime-text.text-darken-1{color:#c0ca33 !important}.lime.darken-2{background-color:#afb42b !important}.lime-text.text-darken-2{color:#afb42b !important}.lime.darken-3{background-color:#9e9d24 !important}.lime-text.text-darken-3{color:#9e9d24 !important}.lime.darken-4{background-color:#827717 !important}.lime-text.text-darken-4{color:#827717 !important}.lime.accent-1{background-color:#f4ff81 !important}.lime-text.text-accent-1{color:#f4ff81 !important}.lime.accent-2{background-color:#eeff41 !important}.lime-text.text-accent-2{color:#eeff41 !important}.lime.accent-3{background-color:#c6ff00 !important}.lime-text.text-accent-3{color:#c6ff00 !important}.lime.accent-4{background-color:#aeea00 !important}.lime-text.text-accent-4{color:#aeea00 !important}.yellow{background-color:#ffeb3b !important}.yellow-text{color:#ffeb3b !important}.yellow.lighten-5{background-color:#fffde7 !important}.yellow-text.text-lighten-5{color:#fffde7 !important}.yellow.lighten-4{background-color:#fff9c4 !important}.yellow-text.text-lighten-4{color:#fff9c4 !important}.yellow.lighten-3{background-color:#fff59d !important}.yellow-text.text-lighten-3{color:#fff59d !important}.yellow.lighten-2{background-color:#fff176 !important}.yellow-text.text-lighten-2{color:#fff176 !important}.yellow.lighten-1{background-color:#ffee58 !important}.yellow-text.text-lighten-1{color:#ffee58 !important}.yellow.darken-1{background-color:#fdd835 !important}.yellow-text.text-darken-1{color:#fdd835 !important}.yellow.darken-2{background-color:#fbc02d !important}.yellow-text.text-darken-2{color:#fbc02d !important}.yellow.darken-3{background-color:#f9a825 !important}.yellow-text.text-darken-3{color:#f9a825 !important}.yellow.darken-4{background-color:#f57f17 !important}.yellow-text.text-darken-4{color:#f57f17 !important}.yellow.accent-1{background-color:#ffff8d !important}.yellow-text.text-accent-1{color:#ffff8d !important}.yellow.accent-2{background-color:#ff0 !important}.yellow-text.text-accent-2{color:#ff0 !important}.yellow.accent-3{background-color:#ffea00 !important}.yellow-text.text-accent-3{color:#ffea00 !important}.yellow.accent-4{background-color:#ffd600 !important}.yellow-text.text-accent-4{color:#ffd600 !important}.amber{background-color:#ffc107 !important}.amber-text{color:#ffc107 !important}.amber.lighten-5{background-color:#fff8e1 !important}.amber-text.text-lighten-5{color:#fff8e1 !important}.amber.lighten-4{background-color:#ffecb3 !important}.amber-text.text-lighten-4{color:#ffecb3 !important}.amber.lighten-3{background-color:#ffe082 !important}.amber-text.text-lighten-3{color:#ffe082 !important}.amber.lighten-2{background-color:#ffd54f !important}.amber-text.text-lighten-2{color:#ffd54f !important}.amber.lighten-1{background-color:#ffca28 !important}.amber-text.text-lighten-1{color:#ffca28 !important}.amber.darken-1{background-color:#ffb300 !important}.amber-text.text-darken-1{color:#ffb300 !important}.amber.darken-2{background-color:#ffa000 !important}.amber-text.text-darken-2{color:#ffa000 !important}.amber.darken-3{background-color:#ff8f00 !important}.amber-text.text-darken-3{color:#ff8f00 !important}.amber.darken-4{background-color:#ff6f00 !important}.amber-text.text-darken-4{color:#ff6f00 !important}.amber.accent-1{background-color:#ffe57f !important}.amber-text.text-accent-1{color:#ffe57f !important}.amber.accent-2{background-color:#ffd740 !important}.amber-text.text-accent-2{color:#ffd740 !important}.amber.accent-3{background-color:#ffc400 !important}.amber-text.text-accent-3{color:#ffc400 !important}.amber.accent-4{background-color:#ffab00 !important}.amber-text.text-accent-4{color:#ffab00 !important}.orange{background-color:#ff9800 !important}.orange-text{color:#ff9800 !important}.orange.lighten-5{background-color:#fff3e0 !important}.orange-text.text-lighten-5{color:#fff3e0 !important}.orange.lighten-4{background-color:#ffe0b2 !important}.orange-text.text-lighten-4{color:#ffe0b2 !important}.orange.lighten-3{background-color:#ffcc80 !important}.orange-text.text-lighten-3{color:#ffcc80 !important}.orange.lighten-2{background-color:#ffb74d !important}.orange-text.text-lighten-2{color:#ffb74d !important}.orange.lighten-1{background-color:#ffa726 !important}.orange-text.text-lighten-1{color:#ffa726 !important}.orange.darken-1{background-color:#fb8c00 !important}.orange-text.text-darken-1{color:#fb8c00 !important}.orange.darken-2{background-color:#f57c00 !important}.orange-text.text-darken-2{color:#f57c00 !important}.orange.darken-3{background-color:#ef6c00 !important}.orange-text.text-darken-3{color:#ef6c00 !important}.orange.darken-4{background-color:#e65100 !important}.orange-text.text-darken-4{color:#e65100 !important}.orange.accent-1{background-color:#ffd180 !important}.orange-text.text-accent-1{color:#ffd180 !important}.orange.accent-2{background-color:#ffab40 !important}.orange-text.text-accent-2{color:#ffab40 !important}.orange.accent-3{background-color:#ff9100 !important}.orange-text.text-accent-3{color:#ff9100 !important}.orange.accent-4{background-color:#ff6d00 !important}.orange-text.text-accent-4{color:#ff6d00 !important}.deep-orange{background-color:#ff5722 !important}.deep-orange-text{color:#ff5722 !important}.deep-orange.lighten-5{background-color:#fbe9e7 !important}.deep-orange-text.text-lighten-5{color:#fbe9e7 !important}.deep-orange.lighten-4{background-color:#ffccbc !important}.deep-orange-text.text-lighten-4{color:#ffccbc !important}.deep-orange.lighten-3{background-color:#ffab91 !important}.deep-orange-text.text-lighten-3{color:#ffab91 !important}.deep-orange.lighten-2{background-color:#ff8a65 !important}.deep-orange-text.text-lighten-2{color:#ff8a65 !important}.deep-orange.lighten-1{background-color:#ff7043 !important}.deep-orange-text.text-lighten-1{color:#ff7043 !important}.deep-orange.darken-1{background-color:#f4511e !important}.deep-orange-text.text-darken-1{color:#f4511e !important}.deep-orange.darken-2{background-color:#e64a19 !important}.deep-orange-text.text-darken-2{color:#e64a19 !important}.deep-orange.darken-3{background-color:#d84315 !important}.deep-orange-text.text-darken-3{color:#d84315 !important}.deep-orange.darken-4{background-color:#bf360c !important}.deep-orange-text.text-darken-4{color:#bf360c !important}.deep-orange.accent-1{background-color:#ff9e80 !important}.deep-orange-text.text-accent-1{color:#ff9e80 !important}.deep-orange.accent-2{background-color:#ff6e40 !important}.deep-orange-text.text-accent-2{color:#ff6e40 !important}.deep-orange.accent-3{background-color:#ff3d00 !important}.deep-orange-text.text-accent-3{color:#ff3d00 !important}.deep-orange.accent-4{background-color:#dd2c00 !important}.deep-orange-text.text-accent-4{color:#dd2c00 !important}.brown{background-color:#795548 !important}.brown-text{color:#795548 !important}.brown.lighten-5{background-color:#efebe9 !important}.brown-text.text-lighten-5{color:#efebe9 !important}.brown.lighten-4{background-color:#d7ccc8 !important}.brown-text.text-lighten-4{color:#d7ccc8 !important}.brown.lighten-3{background-color:#bcaaa4 !important}.brown-text.text-lighten-3{color:#bcaaa4 !important}.brown.lighten-2{background-color:#a1887f !important}.brown-text.text-lighten-2{color:#a1887f !important}.brown.lighten-1{background-color:#8d6e63 !important}.brown-text.text-lighten-1{color:#8d6e63 !important}.brown.darken-1{background-color:#6d4c41 !important}.brown-text.text-darken-1{color:#6d4c41 !important}.brown.darken-2{background-color:#5d4037 !important}.brown-text.text-darken-2{color:#5d4037 !important}.brown.darken-3{background-color:#4e342e !important}.brown-text.text-darken-3{color:#4e342e !important}.brown.darken-4{background-color:#3e2723 !important}.brown-text.text-darken-4{color:#3e2723 !important}.blue-grey{background-color:#607d8b !important}.blue-grey-text{color:#607d8b !important}.blue-grey.lighten-5{background-color:#eceff1 !important}.blue-grey-text.text-lighten-5{color:#eceff1 !important}.blue-grey.lighten-4{background-color:#cfd8dc !important}.blue-grey-text.text-lighten-4{color:#cfd8dc !important}.blue-grey.lighten-3{background-color:#b0bec5 !important}.blue-grey-text.text-lighten-3{color:#b0bec5 !important}.blue-grey.lighten-2{background-color:#90a4ae !important}.blue-grey-text.text-lighten-2{color:#90a4ae !important}.blue-grey.lighten-1{background-color:#78909c !important}.blue-grey-text.text-lighten-1{color:#78909c !important}.blue-grey.darken-1{background-color:#546e7a !important}.blue-grey-text.text-darken-1{color:#546e7a !important}.blue-grey.darken-2{background-color:#455a64 !important}.blue-grey-text.text-darken-2{color:#455a64 !important}.blue-grey.darken-3{background-color:#37474f !important}.blue-grey-text.text-darken-3{color:#37474f !important}.blue-grey.darken-4{background-color:#263238 !important}.blue-grey-text.text-darken-4{color:#263238 !important}.grey{background-color:#9e9e9e !important}.grey-text{color:#9e9e9e !important}.grey.lighten-5{background-color:#fafafa !important}.grey-text.text-lighten-5{color:#fafafa !important}.grey.lighten-4{background-color:#f5f5f5 !important}.grey-text.text-lighten-4{color:#f5f5f5 !important}.grey.lighten-3{background-color:#eee !important}.grey-text.text-lighten-3{color:#eee !important}.grey.lighten-2{background-color:#e0e0e0 !important}.grey-text.text-lighten-2{color:#e0e0e0 !important}.grey.lighten-1{background-color:#bdbdbd !important}.grey-text.text-lighten-1{color:#bdbdbd !important}.grey.darken-1{background-color:#757575 !important}.grey-text.text-darken-1{color:#757575 !important}.grey.darken-2{background-color:#616161 !important}.grey-text.text-darken-2{color:#616161 !important}.grey.darken-3{background-color:#424242 !important}.grey-text.text-darken-3{color:#424242 !important}.grey.darken-4{background-color:#212121 !important}.grey-text.text-darken-4{color:#212121 !important}.black{background-color:#000 !important}.black-text{color:#000 !important}.white{background-color:#fff !important}.white-text{color:#fff !important}.transparent{background-color:rgba(0,0,0,0) !important}.transparent-text{color:rgba(0,0,0,0) !important}/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:0.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;box-sizing:inherit}button,input,optgroup,select,textarea{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif}ul:not(.browser-default){padding-left:0;list-style-type:none}ul:not(.browser-default)>li{list-style-type:none}a{color:#039be5;text-decoration:none;-webkit-tap-highlight-color:transparent}.valign-wrapper{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.clearfix{clear:both}.z-depth-0{-webkit-box-shadow:none !important;box-shadow:none !important}.z-depth-1,nav,.card-panel,.card,.toast,.btn,.btn-large,.btn-small,.btn-floating,.dropdown-content,.collapsible,.sidenav{-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2)}.z-depth-1-half,.btn:hover,.btn-large:hover,.btn-small:hover,.btn-floating:hover{-webkit-box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.z-depth-2{-webkit-box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.3);box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.3)}.z-depth-3{-webkit-box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2);box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2)}.z-depth-4{-webkit-box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -7px rgba(0,0,0,0.2);box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -7px rgba(0,0,0,0.2)}.z-depth-5,.modal{-webkit-box-shadow:0 24px 38px 3px rgba(0,0,0,0.14),0 9px 46px 8px rgba(0,0,0,0.12),0 11px 15px -7px rgba(0,0,0,0.2);box-shadow:0 24px 38px 3px rgba(0,0,0,0.14),0 9px 46px 8px rgba(0,0,0,0.12),0 11px 15px -7px rgba(0,0,0,0.2)}.hoverable{-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s}.hoverable:hover{-webkit-box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}.divider{height:1px;overflow:hidden;background-color:#e0e0e0}blockquote{margin:20px 0;padding-left:1.5rem;border-left:5px solid #ee6e73}i{line-height:inherit}i.left{float:left;margin-right:15px}i.right{float:right;margin-left:15px}i.tiny{font-size:1rem}i.small{font-size:2rem}i.medium{font-size:4rem}i.large{font-size:6rem}img.responsive-img,video.responsive-video{max-width:100%;height:auto}.pagination li{display:inline-block;border-radius:2px;text-align:center;vertical-align:top;height:30px}.pagination li a{color:#444;display:inline-block;font-size:1.2rem;padding:0 10px;line-height:30px}.pagination li.active a{color:#fff}.pagination li.active{background-color:#ee6e73}.pagination li.disabled a{cursor:default;color:#999}.pagination li i{font-size:2rem}.pagination li.pages ul li{display:inline-block;float:none}@media only screen and (max-width: 992px){.pagination{width:100%}.pagination li.prev,.pagination li.next{width:10%}.pagination li.pages{width:80%;overflow:hidden;white-space:nowrap}}.breadcrumb{font-size:18px;color:rgba(255,255,255,0.7)}.breadcrumb i,.breadcrumb [class^="mdi-"],.breadcrumb [class*="mdi-"],.breadcrumb i.material-icons{display:inline-block;float:left;font-size:24px}.breadcrumb:before{content:'\E5CC';color:rgba(255,255,255,0.7);vertical-align:top;display:inline-block;font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:25px;margin:0 10px 0 8px;-webkit-font-smoothing:antialiased}.breadcrumb:first-child:before{display:none}.breadcrumb:last-child{color:#fff}.parallax-container{position:relative;overflow:hidden;height:500px}.parallax-container .parallax{position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}.parallax-container .parallax img{opacity:0;position:absolute;left:50%;bottom:0;min-width:100%;min-height:100%;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);-webkit-transform:translateX(-50%);transform:translateX(-50%)}.pin-top,.pin-bottom{position:relative}.pinned{position:fixed !important}ul.staggered-list li{opacity:0}.fade-in{opacity:0;-webkit-transform-origin:0 50%;transform-origin:0 50%}@media only screen and (max-width: 600px){.hide-on-small-only,.hide-on-small-and-down{display:none !important}}@media only screen and (max-width: 992px){.hide-on-med-and-down{display:none !important}}@media only screen and (min-width: 601px){.hide-on-med-and-up{display:none !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.hide-on-med-only{display:none !important}}@media only screen and (min-width: 993px){.hide-on-large-only{display:none !important}}@media only screen and (min-width: 1201px){.hide-on-extra-large-only{display:none !important}}@media only screen and (min-width: 1201px){.show-on-extra-large{display:block !important}}@media only screen and (min-width: 993px){.show-on-large{display:block !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.show-on-medium{display:block !important}}@media only screen and (max-width: 600px){.show-on-small{display:block !important}}@media only screen and (min-width: 601px){.show-on-medium-and-up{display:block !important}}@media only screen and (max-width: 992px){.show-on-medium-and-down{display:block !important}}@media only screen and (max-width: 600px){.center-on-small-only{text-align:center}}.page-footer{padding-top:20px;color:#fff;background-color:#ee6e73}.page-footer .footer-copyright{overflow:hidden;min-height:50px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:10px 0px;color:rgba(255,255,255,0.8);background-color:rgba(51,51,51,0.08)}table,th,td{border:none}table{width:100%;display:table;border-collapse:collapse;border-spacing:0}table.striped tr{border-bottom:none}table.striped>tbody>tr:nth-child(odd){background-color:rgba(242,242,242,0.5)}table.striped>tbody>tr>td{border-radius:0}table.highlight>tbody>tr{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}table.highlight>tbody>tr:hover{background-color:rgba(242,242,242,0.5)}table.centered thead tr th,table.centered tbody tr td{text-align:center}tr{border-bottom:1px solid rgba(0,0,0,0.12)}td,th{padding:15px 5px;display:table-cell;text-align:left;vertical-align:middle;border-radius:2px}@media only screen and (max-width: 992px){table.responsive-table{width:100%;border-collapse:collapse;border-spacing:0;display:block;position:relative}table.responsive-table td:empty:before{content:'\00a0'}table.responsive-table th,table.responsive-table td{margin:0;vertical-align:top}table.responsive-table th{text-align:left}table.responsive-table thead{display:block;float:left}table.responsive-table thead tr{display:block;padding:0 10px 0 0}table.responsive-table thead tr th::before{content:"\00a0"}table.responsive-table tbody{display:block;width:auto;position:relative;overflow-x:auto;white-space:nowrap}table.responsive-table tbody tr{display:inline-block;vertical-align:top}table.responsive-table th{display:block;text-align:right}table.responsive-table td{display:block;min-height:1.25em;text-align:left}table.responsive-table tr{border-bottom:none;padding:0 10px}table.responsive-table thead{border:0;border-right:1px solid rgba(0,0,0,0.12)}}.collection{margin:.5rem 0 1rem 0;border:1px solid #e0e0e0;border-radius:2px;overflow:hidden;position:relative}.collection .collection-item{background-color:#fff;line-height:1.5rem;padding:10px 20px;margin:0;border-bottom:1px solid #e0e0e0}.collection .collection-item.avatar{min-height:84px;padding-left:72px;position:relative}.collection .collection-item.avatar:not(.circle-clipper)>.circle,.collection .collection-item.avatar :not(.circle-clipper)>.circle{position:absolute;width:42px;height:42px;overflow:hidden;left:15px;display:inline-block;vertical-align:middle}.collection .collection-item.avatar i.circle{font-size:18px;line-height:42px;color:#fff;background-color:#999;text-align:center}.collection .collection-item.avatar .title{font-size:16px}.collection .collection-item.avatar p{margin:0}.collection .collection-item.avatar .secondary-content{position:absolute;top:16px;right:16px}.collection .collection-item:last-child{border-bottom:none}.collection .collection-item.active{background-color:#26a69a;color:#eafaf9}.collection .collection-item.active .secondary-content{color:#fff}.collection a.collection-item{display:block;-webkit-transition:.25s;transition:.25s;color:#26a69a}.collection a.collection-item:not(.active):hover{background-color:#ddd}.collection.with-header .collection-header{background-color:#fff;border-bottom:1px solid #e0e0e0;padding:10px 20px}.collection.with-header .collection-item{padding-left:30px}.collection.with-header .collection-item.avatar{padding-left:72px}.secondary-content{float:right;color:#26a69a}.collapsible .collection{margin:0;border:none}.video-container{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.video-container iframe,.video-container object,.video-container embed{position:absolute;top:0;left:0;width:100%;height:100%}.progress{position:relative;height:4px;display:block;width:100%;background-color:#acece6;border-radius:2px;margin:.5rem 0 1rem 0;overflow:hidden}.progress .determinate{position:absolute;top:0;left:0;bottom:0;background-color:#26a69a;-webkit-transition:width .3s linear;transition:width .3s linear}.progress .indeterminate{background-color:#26a69a}.progress .indeterminate:before{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite}.progress .indeterminate:after{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;-webkit-animation-delay:1.15s;animation-delay:1.15s}@-webkit-keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@-webkit-keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}.hide{display:none !important}.left-align{text-align:left}.right-align{text-align:right}.center,.center-align{text-align:center}.left{float:left !important}.right{float:right !important}.no-select,input[type=range],input[type=range]+.thumb{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.circle{border-radius:50%}.center-block{display:block;margin-left:auto;margin-right:auto}.truncate{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-padding{padding:0 !important}span.badge{min-width:3rem;padding:0 6px;margin-left:14px;text-align:center;font-size:1rem;line-height:22px;height:22px;color:#757575;float:right;-webkit-box-sizing:border-box;box-sizing:border-box}span.badge.new{font-weight:300;font-size:0.8rem;color:#fff;background-color:#26a69a;border-radius:2px}span.badge.new:after{content:" new"}span.badge[data-badge-caption]::after{content:" " attr(data-badge-caption)}nav ul a span.badge{display:inline-block;float:none;margin-left:4px;line-height:22px;height:22px;-webkit-font-smoothing:auto}.collection-item span.badge{margin-top:calc(.75rem - 11px)}.collapsible span.badge{margin-left:auto}.sidenav span.badge{margin-top:calc(24px - 11px)}table span.badge{display:inline-block;float:none;margin-left:auto}.material-icons{text-rendering:optimizeLegibility;-webkit-font-feature-settings:'liga';-moz-font-feature-settings:'liga';font-feature-settings:'liga'}.container{margin:0 auto;max-width:1280px;width:90%}@media only screen and (min-width: 601px){.container{width:85%}}@media only screen and (min-width: 993px){.container{width:70%}}.col .row{margin-left:-.75rem;margin-right:-.75rem}.section{padding-top:1rem;padding-bottom:1rem}.section.no-pad{padding:0}.section.no-pad-bot{padding-bottom:0}.section.no-pad-top{padding-top:0}.row{margin-left:auto;margin-right:auto;margin-bottom:20px}.row:after{content:"";display:table;clear:both}.row .col{float:left;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0 .75rem;min-height:1px}.row .col[class*="push-"],.row .col[class*="pull-"]{position:relative}.row .col.s1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.s4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.s7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.s10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-s1{margin-left:8.3333333333%}.row .col.pull-s1{right:8.3333333333%}.row .col.push-s1{left:8.3333333333%}.row .col.offset-s2{margin-left:16.6666666667%}.row .col.pull-s2{right:16.6666666667%}.row .col.push-s2{left:16.6666666667%}.row .col.offset-s3{margin-left:25%}.row .col.pull-s3{right:25%}.row .col.push-s3{left:25%}.row .col.offset-s4{margin-left:33.3333333333%}.row .col.pull-s4{right:33.3333333333%}.row .col.push-s4{left:33.3333333333%}.row .col.offset-s5{margin-left:41.6666666667%}.row .col.pull-s5{right:41.6666666667%}.row .col.push-s5{left:41.6666666667%}.row .col.offset-s6{margin-left:50%}.row .col.pull-s6{right:50%}.row .col.push-s6{left:50%}.row .col.offset-s7{margin-left:58.3333333333%}.row .col.pull-s7{right:58.3333333333%}.row .col.push-s7{left:58.3333333333%}.row .col.offset-s8{margin-left:66.6666666667%}.row .col.pull-s8{right:66.6666666667%}.row .col.push-s8{left:66.6666666667%}.row .col.offset-s9{margin-left:75%}.row .col.pull-s9{right:75%}.row .col.push-s9{left:75%}.row .col.offset-s10{margin-left:83.3333333333%}.row .col.pull-s10{right:83.3333333333%}.row .col.push-s10{left:83.3333333333%}.row .col.offset-s11{margin-left:91.6666666667%}.row .col.pull-s11{right:91.6666666667%}.row .col.push-s11{left:91.6666666667%}.row .col.offset-s12{margin-left:100%}.row .col.pull-s12{right:100%}.row .col.push-s12{left:100%}@media only screen and (min-width: 601px){.row .col.m1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.m4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.m7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.m10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-m1{margin-left:8.3333333333%}.row .col.pull-m1{right:8.3333333333%}.row .col.push-m1{left:8.3333333333%}.row .col.offset-m2{margin-left:16.6666666667%}.row .col.pull-m2{right:16.6666666667%}.row .col.push-m2{left:16.6666666667%}.row .col.offset-m3{margin-left:25%}.row .col.pull-m3{right:25%}.row .col.push-m3{left:25%}.row .col.offset-m4{margin-left:33.3333333333%}.row .col.pull-m4{right:33.3333333333%}.row .col.push-m4{left:33.3333333333%}.row .col.offset-m5{margin-left:41.6666666667%}.row .col.pull-m5{right:41.6666666667%}.row .col.push-m5{left:41.6666666667%}.row .col.offset-m6{margin-left:50%}.row .col.pull-m6{right:50%}.row .col.push-m6{left:50%}.row .col.offset-m7{margin-left:58.3333333333%}.row .col.pull-m7{right:58.3333333333%}.row .col.push-m7{left:58.3333333333%}.row .col.offset-m8{margin-left:66.6666666667%}.row .col.pull-m8{right:66.6666666667%}.row .col.push-m8{left:66.6666666667%}.row .col.offset-m9{margin-left:75%}.row .col.pull-m9{right:75%}.row .col.push-m9{left:75%}.row .col.offset-m10{margin-left:83.3333333333%}.row .col.pull-m10{right:83.3333333333%}.row .col.push-m10{left:83.3333333333%}.row .col.offset-m11{margin-left:91.6666666667%}.row .col.pull-m11{right:91.6666666667%}.row .col.push-m11{left:91.6666666667%}.row .col.offset-m12{margin-left:100%}.row .col.pull-m12{right:100%}.row .col.push-m12{left:100%}}@media only screen and (min-width: 993px){.row .col.l1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.l4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.l7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.l10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-l1{margin-left:8.3333333333%}.row .col.pull-l1{right:8.3333333333%}.row .col.push-l1{left:8.3333333333%}.row .col.offset-l2{margin-left:16.6666666667%}.row .col.pull-l2{right:16.6666666667%}.row .col.push-l2{left:16.6666666667%}.row .col.offset-l3{margin-left:25%}.row .col.pull-l3{right:25%}.row .col.push-l3{left:25%}.row .col.offset-l4{margin-left:33.3333333333%}.row .col.pull-l4{right:33.3333333333%}.row .col.push-l4{left:33.3333333333%}.row .col.offset-l5{margin-left:41.6666666667%}.row .col.pull-l5{right:41.6666666667%}.row .col.push-l5{left:41.6666666667%}.row .col.offset-l6{margin-left:50%}.row .col.pull-l6{right:50%}.row .col.push-l6{left:50%}.row .col.offset-l7{margin-left:58.3333333333%}.row .col.pull-l7{right:58.3333333333%}.row .col.push-l7{left:58.3333333333%}.row .col.offset-l8{margin-left:66.6666666667%}.row .col.pull-l8{right:66.6666666667%}.row .col.push-l8{left:66.6666666667%}.row .col.offset-l9{margin-left:75%}.row .col.pull-l9{right:75%}.row .col.push-l9{left:75%}.row .col.offset-l10{margin-left:83.3333333333%}.row .col.pull-l10{right:83.3333333333%}.row .col.push-l10{left:83.3333333333%}.row .col.offset-l11{margin-left:91.6666666667%}.row .col.pull-l11{right:91.6666666667%}.row .col.push-l11{left:91.6666666667%}.row .col.offset-l12{margin-left:100%}.row .col.pull-l12{right:100%}.row .col.push-l12{left:100%}}@media only screen and (min-width: 1201px){.row .col.xl1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.xl4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.xl7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.xl10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.xl11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.xl12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-xl1{margin-left:8.3333333333%}.row .col.pull-xl1{right:8.3333333333%}.row .col.push-xl1{left:8.3333333333%}.row .col.offset-xl2{margin-left:16.6666666667%}.row .col.pull-xl2{right:16.6666666667%}.row .col.push-xl2{left:16.6666666667%}.row .col.offset-xl3{margin-left:25%}.row .col.pull-xl3{right:25%}.row .col.push-xl3{left:25%}.row .col.offset-xl4{margin-left:33.3333333333%}.row .col.pull-xl4{right:33.3333333333%}.row .col.push-xl4{left:33.3333333333%}.row .col.offset-xl5{margin-left:41.6666666667%}.row .col.pull-xl5{right:41.6666666667%}.row .col.push-xl5{left:41.6666666667%}.row .col.offset-xl6{margin-left:50%}.row .col.pull-xl6{right:50%}.row .col.push-xl6{left:50%}.row .col.offset-xl7{margin-left:58.3333333333%}.row .col.pull-xl7{right:58.3333333333%}.row .col.push-xl7{left:58.3333333333%}.row .col.offset-xl8{margin-left:66.6666666667%}.row .col.pull-xl8{right:66.6666666667%}.row .col.push-xl8{left:66.6666666667%}.row .col.offset-xl9{margin-left:75%}.row .col.pull-xl9{right:75%}.row .col.push-xl9{left:75%}.row .col.offset-xl10{margin-left:83.3333333333%}.row .col.pull-xl10{right:83.3333333333%}.row .col.push-xl10{left:83.3333333333%}.row .col.offset-xl11{margin-left:91.6666666667%}.row .col.pull-xl11{right:91.6666666667%}.row .col.push-xl11{left:91.6666666667%}.row .col.offset-xl12{margin-left:100%}.row .col.pull-xl12{right:100%}.row .col.push-xl12{left:100%}}nav{color:#fff;background-color:#ee6e73;width:100%;height:56px;line-height:56px}nav.nav-extended{height:auto}nav.nav-extended .nav-wrapper{min-height:56px;height:auto}nav.nav-extended .nav-content{position:relative;line-height:normal}nav a{color:#fff}nav i,nav [class^="mdi-"],nav [class*="mdi-"],nav i.material-icons{display:block;font-size:24px;height:56px;line-height:56px}nav .nav-wrapper{position:relative;height:100%}@media only screen and (min-width: 993px){nav a.sidenav-trigger{display:none}}nav .sidenav-trigger{float:left;position:relative;z-index:1;height:56px;margin:0 18px}nav .sidenav-trigger i{height:56px;line-height:56px}nav .brand-logo{position:absolute;color:#fff;display:inline-block;font-size:2.1rem;padding:0}nav .brand-logo.center{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}@media only screen and (max-width: 992px){nav .brand-logo{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}nav .brand-logo.left,nav .brand-logo.right{padding:0;-webkit-transform:none;transform:none}nav .brand-logo.left{left:0.5rem}nav .brand-logo.right{right:0.5rem;left:auto}}nav .brand-logo.right{right:0.5rem;padding:0}nav .brand-logo i,nav .brand-logo [class^="mdi-"],nav .brand-logo [class*="mdi-"],nav .brand-logo i.material-icons{float:left;margin-right:15px}nav .nav-title{display:inline-block;font-size:32px;padding:28px 0}nav ul{margin:0}nav ul li{-webkit-transition:background-color .3s;transition:background-color .3s;float:left;padding:0}nav ul li.active{background-color:rgba(0,0,0,0.1)}nav ul a{-webkit-transition:background-color .3s;transition:background-color .3s;font-size:1rem;color:#fff;display:block;padding:0 15px;cursor:pointer}nav ul a.btn,nav ul a.btn-large,nav ul a.btn-small,nav ul a.btn-large,nav ul a.btn-flat,nav ul a.btn-floating{margin-top:-2px;margin-left:15px;margin-right:15px}nav ul a.btn>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-small>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-flat>.material-icons,nav ul a.btn-floating>.material-icons{height:inherit;line-height:inherit}nav ul a:hover{background-color:rgba(0,0,0,0.1)}nav ul.left{float:left}nav form{height:100%}nav .input-field{margin:0;height:100%}nav .input-field input{height:100%;font-size:1.2rem;border:none;padding-left:2rem}nav .input-field input:focus,nav .input-field input[type=text]:valid,nav .input-field input[type=password]:valid,nav .input-field input[type=email]:valid,nav .input-field input[type=url]:valid,nav .input-field input[type=date]:valid{border:none;-webkit-box-shadow:none;box-shadow:none}nav .input-field label{top:0;left:0}nav .input-field label i{color:rgba(255,255,255,0.7);-webkit-transition:color .3s;transition:color .3s}nav .input-field label.active i{color:#fff}.navbar-fixed{position:relative;height:56px;z-index:997}.navbar-fixed nav{position:fixed}@media only screen and (min-width: 601px){nav.nav-extended .nav-wrapper{min-height:64px}nav,nav .nav-wrapper i,nav a.sidenav-trigger,nav a.sidenav-trigger i{height:64px;line-height:64px}.navbar-fixed{height:64px}}a{text-decoration:none}html{line-height:1.5;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-weight:normal;color:rgba(0,0,0,0.87)}@media only screen and (min-width: 0){html{font-size:14px}}@media only screen and (min-width: 992px){html{font-size:14.5px}}@media only screen and (min-width: 1200px){html{font-size:15px}}h1,h2,h3,h4,h5,h6{font-weight:400;line-height:1.3}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{font-weight:inherit}h1{font-size:4.2rem;line-height:110%;margin:2.8rem 0 1.68rem 0}h2{font-size:3.56rem;line-height:110%;margin:2.3733333333rem 0 1.424rem 0}h3{font-size:2.92rem;line-height:110%;margin:1.9466666667rem 0 1.168rem 0}h4{font-size:2.28rem;line-height:110%;margin:1.52rem 0 .912rem 0}h5{font-size:1.64rem;line-height:110%;margin:1.0933333333rem 0 .656rem 0}h6{font-size:1.15rem;line-height:110%;margin:.7666666667rem 0 .46rem 0}em{font-style:italic}strong{font-weight:500}small{font-size:75%}.light{font-weight:300}.thin{font-weight:200}@media only screen and (min-width: 360px){.flow-text{font-size:1.2rem}}@media only screen and (min-width: 390px){.flow-text{font-size:1.224rem}}@media only screen and (min-width: 420px){.flow-text{font-size:1.248rem}}@media only screen and (min-width: 450px){.flow-text{font-size:1.272rem}}@media only screen and (min-width: 480px){.flow-text{font-size:1.296rem}}@media only screen and (min-width: 510px){.flow-text{font-size:1.32rem}}@media only screen and (min-width: 540px){.flow-text{font-size:1.344rem}}@media only screen and (min-width: 570px){.flow-text{font-size:1.368rem}}@media only screen and (min-width: 600px){.flow-text{font-size:1.392rem}}@media only screen and (min-width: 630px){.flow-text{font-size:1.416rem}}@media only screen and (min-width: 660px){.flow-text{font-size:1.44rem}}@media only screen and (min-width: 690px){.flow-text{font-size:1.464rem}}@media only screen and (min-width: 720px){.flow-text{font-size:1.488rem}}@media only screen and (min-width: 750px){.flow-text{font-size:1.512rem}}@media only screen and (min-width: 780px){.flow-text{font-size:1.536rem}}@media only screen and (min-width: 810px){.flow-text{font-size:1.56rem}}@media only screen and (min-width: 840px){.flow-text{font-size:1.584rem}}@media only screen and (min-width: 870px){.flow-text{font-size:1.608rem}}@media only screen and (min-width: 900px){.flow-text{font-size:1.632rem}}@media only screen and (min-width: 930px){.flow-text{font-size:1.656rem}}@media only screen and (min-width: 960px){.flow-text{font-size:1.68rem}}@media only screen and (max-width: 360px){.flow-text{font-size:1.2rem}}.scale-transition{-webkit-transition:-webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:-webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63), -webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important}.scale-transition.scale-out{-webkit-transform:scale(0);transform:scale(0);-webkit-transition:-webkit-transform .2s !important;transition:-webkit-transform .2s !important;transition:transform .2s !important;transition:transform .2s, -webkit-transform .2s !important}.scale-transition.scale-in{-webkit-transform:scale(1);transform:scale(1)}.card-panel{-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s;padding:24px;margin:.5rem 0 1rem 0;border-radius:2px;background-color:#fff}.card{position:relative;margin:.5rem 0 1rem 0;background-color:#fff;-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s, -webkit-box-shadow .25s;border-radius:2px}.card .card-title{font-size:24px;font-weight:300}.card .card-title.activator{cursor:pointer}.card.small,.card.medium,.card.large{position:relative}.card.small .card-image,.card.medium .card-image,.card.large .card-image{max-height:60%;overflow:hidden}.card.small .card-image+.card-content,.card.medium .card-image+.card-content,.card.large .card-image+.card-content{max-height:40%}.card.small .card-content,.card.medium .card-content,.card.large .card-content{max-height:100%;overflow:hidden}.card.small .card-action,.card.medium .card-action,.card.large .card-action{position:absolute;bottom:0;left:0;right:0}.card.small{height:300px}.card.medium{height:400px}.card.large{height:500px}.card.horizontal{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.card.horizontal.small .card-image,.card.horizontal.medium .card-image,.card.horizontal.large .card-image{height:100%;max-height:none;overflow:visible}.card.horizontal.small .card-image img,.card.horizontal.medium .card-image img,.card.horizontal.large .card-image img{height:100%}.card.horizontal .card-image{max-width:50%}.card.horizontal .card-image img{border-radius:2px 0 0 2px;max-width:100%;width:auto}.card.horizontal .card-stacked{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;position:relative}.card.horizontal .card-stacked .card-content{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.card.sticky-action .card-action{z-index:2}.card.sticky-action .card-reveal{z-index:1;padding-bottom:64px}.card .card-image{position:relative}.card .card-image img{display:block;border-radius:2px 2px 0 0;position:relative;left:0;right:0;top:0;bottom:0;width:100%}.card .card-image .card-title{color:#fff;position:absolute;bottom:0;left:0;max-width:100%;padding:24px}.card .card-content{padding:24px;border-radius:0 0 2px 2px}.card .card-content p{margin:0}.card .card-content .card-title{display:block;line-height:32px;margin-bottom:8px}.card .card-content .card-title i{line-height:32px}.card .card-action{background-color:inherit;border-top:1px solid rgba(160,160,160,0.2);position:relative;padding:16px 24px}.card .card-action:last-child{border-radius:0 0 2px 2px}.card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating){color:#ffab40;margin-right:24px;-webkit-transition:color .3s ease;transition:color .3s ease;text-transform:uppercase}.card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating):hover{color:#ffd8a6}.card .card-reveal{padding:24px;position:absolute;background-color:#fff;width:100%;overflow-y:auto;left:0;top:100%;height:100%;z-index:3;display:none}.card .card-reveal .card-title{cursor:pointer;display:block}#toast-container{display:block;position:fixed;z-index:10000}@media only screen and (max-width: 600px){#toast-container{min-width:100%;bottom:0%}}@media only screen and (min-width: 601px) and (max-width: 992px){#toast-container{left:5%;bottom:7%;max-width:90%}}@media only screen and (min-width: 993px){#toast-container{top:10%;right:7%;max-width:86%}}.toast{border-radius:2px;top:35px;width:auto;margin-top:10px;position:relative;max-width:100%;height:auto;min-height:48px;line-height:1.5em;background-color:#323232;padding:10px 25px;font-size:1.1rem;font-weight:300;color:#fff;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;cursor:default}.toast .toast-action{color:#eeff41;font-weight:500;margin-right:-25px;margin-left:3rem}.toast.rounded{border-radius:24px}@media only screen and (max-width: 600px){.toast{width:100%;border-radius:0}}.tabs{position:relative;overflow-x:auto;overflow-y:hidden;height:48px;width:100%;background-color:#fff;margin:0 auto;white-space:nowrap}.tabs.tabs-transparent{background-color:transparent}.tabs.tabs-transparent .tab a,.tabs.tabs-transparent .tab.disabled a,.tabs.tabs-transparent .tab.disabled a:hover{color:rgba(255,255,255,0.7)}.tabs.tabs-transparent .tab a:hover,.tabs.tabs-transparent .tab a.active{color:#fff}.tabs.tabs-transparent .indicator{background-color:#fff}.tabs.tabs-fixed-width{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs.tabs-fixed-width .tab{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab{display:inline-block;text-align:center;line-height:48px;height:48px;padding:0;margin:0;text-transform:uppercase}.tabs .tab a{color:rgba(238,110,115,0.7);display:block;width:100%;height:100%;padding:0 24px;font-size:14px;text-overflow:ellipsis;overflow:hidden;-webkit-transition:color .28s ease, background-color .28s ease;transition:color .28s ease, background-color .28s ease}.tabs .tab a:focus,.tabs .tab a:focus.active{background-color:rgba(246,178,181,0.2);outline:none}.tabs .tab a:hover,.tabs .tab a.active{background-color:transparent;color:#ee6e73}.tabs .tab.disabled a,.tabs .tab.disabled a:hover{color:rgba(238,110,115,0.4);cursor:default}.tabs .indicator{position:absolute;bottom:0;height:2px;background-color:#f6b2b5;will-change:left, right}@media only screen and (max-width: 992px){.tabs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs .tab{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab a{padding:0 12px}}.material-tooltip{padding:10px 8px;font-size:1rem;z-index:2000;background-color:transparent;border-radius:2px;color:#fff;min-height:36px;line-height:120%;opacity:0;position:absolute;text-align:center;max-width:calc(100% - 4px);overflow:hidden;left:0;top:0;pointer-events:none;visibility:hidden;background-color:#323232}.backdrop{position:absolute;opacity:0;height:7px;width:14px;border-radius:0 0 50% 50%;background-color:#323232;z-index:-1;-webkit-transform-origin:50% 0%;transform-origin:50% 0%;visibility:hidden}.btn,.btn-large,.btn-small,.btn-flat{border:none;border-radius:2px;display:inline-block;height:36px;line-height:36px;padding:0 16px;text-transform:uppercase;vertical-align:middle;-webkit-tap-highlight-color:transparent}.btn.disabled,.disabled.btn-large,.disabled.btn-small,.btn-floating.disabled,.btn-large.disabled,.btn-small.disabled,.btn-flat.disabled,.btn:disabled,.btn-large:disabled,.btn-small:disabled,.btn-floating:disabled,.btn-large:disabled,.btn-small:disabled,.btn-flat:disabled,.btn[disabled],.btn-large[disabled],.btn-small[disabled],.btn-floating[disabled],.btn-large[disabled],.btn-small[disabled],.btn-flat[disabled]{pointer-events:none;background-color:#DFDFDF !important;-webkit-box-shadow:none;box-shadow:none;color:#9F9F9F !important;cursor:default}.btn.disabled:hover,.disabled.btn-large:hover,.disabled.btn-small:hover,.btn-floating.disabled:hover,.btn-large.disabled:hover,.btn-small.disabled:hover,.btn-flat.disabled:hover,.btn:disabled:hover,.btn-large:disabled:hover,.btn-small:disabled:hover,.btn-floating:disabled:hover,.btn-large:disabled:hover,.btn-small:disabled:hover,.btn-flat:disabled:hover,.btn[disabled]:hover,.btn-large[disabled]:hover,.btn-small[disabled]:hover,.btn-floating[disabled]:hover,.btn-large[disabled]:hover,.btn-small[disabled]:hover,.btn-flat[disabled]:hover{background-color:#DFDFDF !important;color:#9F9F9F !important}.btn,.btn-large,.btn-small,.btn-floating,.btn-large,.btn-small,.btn-flat{font-size:14px;outline:0}.btn i,.btn-large i,.btn-small i,.btn-floating i,.btn-large i,.btn-small i,.btn-flat i{font-size:1.3rem;line-height:inherit}.btn:focus,.btn-large:focus,.btn-small:focus,.btn-floating:focus{background-color:#1d7d74}.btn,.btn-large,.btn-small{text-decoration:none;color:#fff;background-color:#26a69a;text-align:center;letter-spacing:.5px;-webkit-transition:background-color .2s ease-out;transition:background-color .2s ease-out;cursor:pointer}.btn:hover,.btn-large:hover,.btn-small:hover{background-color:#2bbbad}.btn-floating{display:inline-block;color:#fff;position:relative;overflow:hidden;z-index:1;width:40px;height:40px;line-height:40px;padding:0;background-color:#26a69a;border-radius:50%;-webkit-transition:background-color .3s;transition:background-color .3s;cursor:pointer;vertical-align:middle}.btn-floating:hover{background-color:#26a69a}.btn-floating:before{border-radius:0}.btn-floating.btn-large{width:56px;height:56px;padding:0}.btn-floating.btn-large.halfway-fab{bottom:-28px}.btn-floating.btn-large i{line-height:56px}.btn-floating.btn-small{width:32.4px;height:32.4px}.btn-floating.btn-small.halfway-fab{bottom:-16.2px}.btn-floating.btn-small i{line-height:32.4px}.btn-floating.halfway-fab{position:absolute;right:24px;bottom:-20px}.btn-floating.halfway-fab.left{right:auto;left:24px}.btn-floating i{width:inherit;display:inline-block;text-align:center;color:#fff;font-size:1.6rem;line-height:40px}button.btn-floating{border:none}.fixed-action-btn{position:fixed;right:23px;bottom:23px;padding-top:15px;margin-bottom:0;z-index:997}.fixed-action-btn.active ul{visibility:visible}.fixed-action-btn.direction-left,.fixed-action-btn.direction-right{padding:0 0 0 15px}.fixed-action-btn.direction-left ul,.fixed-action-btn.direction-right ul{text-align:right;right:64px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);height:100%;left:auto;width:500px}.fixed-action-btn.direction-left ul li,.fixed-action-btn.direction-right ul li{display:inline-block;margin:7.5px 15px 0 0}.fixed-action-btn.direction-right{padding:0 15px 0 0}.fixed-action-btn.direction-right ul{text-align:left;direction:rtl;left:64px;right:auto}.fixed-action-btn.direction-right ul li{margin:7.5px 0 0 15px}.fixed-action-btn.direction-bottom{padding:0 0 15px 0}.fixed-action-btn.direction-bottom ul{top:64px;bottom:auto;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:reverse;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.fixed-action-btn.direction-bottom ul li{margin:15px 0 0 0}.fixed-action-btn.toolbar{padding:0;height:56px}.fixed-action-btn.toolbar.active>a i{opacity:0}.fixed-action-btn.toolbar ul{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;top:0;bottom:0;z-index:1}.fixed-action-btn.toolbar ul li{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;display:inline-block;margin:0;height:100%;-webkit-transition:none;transition:none}.fixed-action-btn.toolbar ul li a{display:block;overflow:hidden;position:relative;width:100%;height:100%;background-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff;line-height:56px;z-index:1}.fixed-action-btn.toolbar ul li a i{line-height:inherit}.fixed-action-btn ul{left:0;right:0;text-align:center;position:absolute;bottom:64px;margin:0;visibility:hidden}.fixed-action-btn ul li{margin-bottom:15px}.fixed-action-btn ul a.btn-floating{opacity:0}.fixed-action-btn .fab-backdrop{position:absolute;top:0;left:0;z-index:-1;width:40px;height:40px;background-color:#26a69a;border-radius:50%;-webkit-transform:scale(0);transform:scale(0)}.btn-flat{-webkit-box-shadow:none;box-shadow:none;background-color:transparent;color:#343434;cursor:pointer;-webkit-transition:background-color .2s;transition:background-color .2s}.btn-flat:focus,.btn-flat:hover{-webkit-box-shadow:none;box-shadow:none}.btn-flat:focus{background-color:rgba(0,0,0,0.1)}.btn-flat.disabled,.btn-flat.btn-flat[disabled]{background-color:transparent !important;color:#b3b2b2 !important;cursor:default}.btn-large{height:54px;line-height:54px;font-size:15px;padding:0 28px}.btn-large i{font-size:1.6rem}.btn-small{height:32.4px;line-height:32.4px;font-size:13px}.btn-small i{font-size:1.2rem}.btn-block{display:block}.dropdown-content{background-color:#fff;margin:0;display:none;min-width:100px;overflow-y:auto;opacity:0;position:absolute;left:0;top:0;z-index:9999;-webkit-transform-origin:0 0;transform-origin:0 0}.dropdown-content:focus{outline:0}.dropdown-content li{clear:both;color:rgba(0,0,0,0.87);cursor:pointer;min-height:50px;line-height:1.5rem;width:100%;text-align:left}.dropdown-content li:hover,.dropdown-content li.active{background-color:#eee}.dropdown-content li:focus{outline:none}.dropdown-content li.divider{min-height:0;height:1px}.dropdown-content li>a,.dropdown-content li>span{font-size:16px;color:#26a69a;display:block;line-height:22px;padding:14px 16px}.dropdown-content li>span>label{top:1px;left:0;height:18px}.dropdown-content li>a>i{height:inherit;line-height:inherit;float:left;margin:0 24px 0 0;width:24px}body.keyboard-focused .dropdown-content li:focus{background-color:#dadada}.input-field.col .dropdown-content [type="checkbox"]+label{top:1px;left:0;height:18px;-webkit-transform:none;transform:none}.dropdown-trigger{cursor:pointer}/*! + * Waves v0.6.0 + * 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;vertical-align:middle;z-index:1;-webkit-transition:.3s ease-out;transition:.3s ease-out}.waves-effect .waves-ripple{position:absolute;border-radius:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;opacity:0;background:rgba(0,0,0,0.2);-webkit-transition:all 0.7s ease-out;transition:all 0.7s ease-out;-webkit-transition-property:opacity, -webkit-transform;transition-property:opacity, -webkit-transform;transition-property:transform, opacity;transition-property:transform, opacity, -webkit-transform;-webkit-transform:scale(0);transform:scale(0);pointer-events:none}.waves-effect.waves-light .waves-ripple{background-color:rgba(255,255,255,0.45)}.waves-effect.waves-red .waves-ripple{background-color:rgba(244,67,54,0.7)}.waves-effect.waves-yellow .waves-ripple{background-color:rgba(255,235,59,0.7)}.waves-effect.waves-orange .waves-ripple{background-color:rgba(255,152,0,0.7)}.waves-effect.waves-purple .waves-ripple{background-color:rgba(156,39,176,0.7)}.waves-effect.waves-green .waves-ripple{background-color:rgba(76,175,80,0.7)}.waves-effect.waves-teal .waves-ripple{background-color:rgba(0,150,136,0.7)}.waves-effect input[type="button"],.waves-effect input[type="reset"],.waves-effect input[type="submit"]{border:0;font-style:normal;font-size:inherit;text-transform:inherit;background:none}.waves-effect img{position:relative;z-index:-1}.waves-notransition{-webkit-transition:none !important;transition:none !important}.waves-circle{-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-mask-image:-webkit-radial-gradient(circle, white 100%, black 100%)}.waves-input-wrapper{border-radius:0.2em;vertical-align:bottom}.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%;-webkit-mask-image:none}.waves-block{display:block}.waves-effect .waves-ripple{z-index:-1}.modal{display:none;position:fixed;left:0;right:0;background-color:#fafafa;padding:0;max-height:70%;width:55%;margin:auto;overflow-y:auto;border-radius:2px;will-change:top, opacity}.modal:focus{outline:none}@media only screen and (max-width: 992px){.modal{width:80%}}.modal h1,.modal h2,.modal h3,.modal h4{margin-top:0}.modal .modal-content{padding:24px}.modal .modal-close{cursor:pointer}.modal .modal-footer{border-radius:0 0 2px 2px;background-color:#fafafa;padding:4px 6px;height:56px;width:100%;text-align:right}.modal .modal-footer .btn,.modal .modal-footer .btn-large,.modal .modal-footer .btn-small,.modal .modal-footer .btn-flat{margin:6px 0}.modal-overlay{position:fixed;z-index:999;top:-25%;left:0;bottom:0;right:0;height:125%;width:100%;background:#000;display:none;will-change:opacity}.modal.modal-fixed-footer{padding:0;height:70%}.modal.modal-fixed-footer .modal-content{position:absolute;height:calc(100% - 56px);max-height:100%;width:100%;overflow-y:auto}.modal.modal-fixed-footer .modal-footer{border-top:1px solid rgba(0,0,0,0.1);position:absolute;bottom:0}.modal.bottom-sheet{top:auto;bottom:-100%;margin:0;width:100%;max-height:45%;border-radius:0;will-change:bottom, opacity}.collapsible{border-top:1px solid #ddd;border-right:1px solid #ddd;border-left:1px solid #ddd;margin:.5rem 0 1rem 0}.collapsible-header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;cursor:pointer;-webkit-tap-highlight-color:transparent;line-height:1.5;padding:1rem;background-color:#fff;border-bottom:1px solid #ddd}.collapsible-header:focus{outline:0}.collapsible-header i{width:2rem;font-size:1.6rem;display:inline-block;text-align:center;margin-right:1rem}.keyboard-focused .collapsible-header:focus{background-color:#eee}.collapsible-body{display:none;border-bottom:1px solid #ddd;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2rem}.sidenav .collapsible,.sidenav.fixed .collapsible{border:none;-webkit-box-shadow:none;box-shadow:none}.sidenav .collapsible li,.sidenav.fixed .collapsible li{padding:0}.sidenav .collapsible-header,.sidenav.fixed .collapsible-header{background-color:transparent;border:none;line-height:inherit;height:inherit;padding:0 16px}.sidenav .collapsible-header:hover,.sidenav.fixed .collapsible-header:hover{background-color:rgba(0,0,0,0.05)}.sidenav .collapsible-header i,.sidenav.fixed .collapsible-header i{line-height:inherit}.sidenav .collapsible-body,.sidenav.fixed .collapsible-body{border:0;background-color:#fff}.sidenav .collapsible-body li a,.sidenav.fixed .collapsible-body li a{padding:0 23.5px 0 31px}.collapsible.popout{border:none;-webkit-box-shadow:none;box-shadow:none}.collapsible.popout>li{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);margin:0 24px;-webkit-transition:margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);transition:margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)}.collapsible.popout>li.active{-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);margin:16px 0}.chip{display:inline-block;height:32px;font-size:13px;font-weight:500;color:rgba(0,0,0,0.6);line-height:32px;padding:0 12px;border-radius:16px;background-color:#e4e4e4;margin-bottom:5px;margin-right:5px}.chip:focus{outline:none;background-color:#26a69a;color:#fff}.chip>img{float:left;margin:0 8px 0 -12px;height:32px;width:32px;border-radius:50%}.chip .close{cursor:pointer;float:right;font-size:16px;line-height:32px;padding-left:8px}.chips{border:none;border-bottom:1px solid #9e9e9e;-webkit-box-shadow:none;box-shadow:none;margin:0 0 8px 0;min-height:45px;outline:none;-webkit-transition:all .3s;transition:all .3s}.chips.focus{border-bottom:1px solid #26a69a;-webkit-box-shadow:0 1px 0 0 #26a69a;box-shadow:0 1px 0 0 #26a69a}.chips:hover{cursor:text}.chips .input{background:none;border:0;color:rgba(0,0,0,0.6);display:inline-block;font-size:16px;height:3rem;line-height:32px;outline:0;margin:0;padding:0 !important;width:120px !important}.chips .input:focus{border:0 !important;-webkit-box-shadow:none !important;box-shadow:none !important}.chips .autocomplete-content{margin-top:0;margin-bottom:0}.prefix ~ .chips{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.chips:empty ~ label{font-size:0.8rem;-webkit-transform:translateY(-140%);transform:translateY(-140%)}.materialboxed{display:block;cursor:-webkit-zoom-in;cursor:zoom-in;position:relative;-webkit-transition:opacity .4s;transition:opacity .4s;-webkit-backface-visibility:hidden}.materialboxed:hover:not(.active){opacity:.8}.materialboxed.active{cursor:-webkit-zoom-out;cursor:zoom-out}#materialbox-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#292929;z-index:1000;will-change:opacity}.materialbox-caption{position:fixed;display:none;color:#fff;line-height:50px;bottom:0;left:0;width:100%;text-align:center;padding:0% 15%;height:50px;z-index:1000;-webkit-font-smoothing:antialiased}select:focus{outline:1px solid #c9f3ef}button:focus{outline:none;background-color:#2ab7a9}label{font-size:.8rem;color:#9e9e9e}::-webkit-input-placeholder{color:#d1d1d1}::-moz-placeholder{color:#d1d1d1}:-ms-input-placeholder{color:#d1d1d1}::-ms-input-placeholder{color:#d1d1d1}::placeholder{color:#d1d1d1}input:not([type]),input[type=text]:not(.browser-default),input[type=password]:not(.browser-default),input[type=email]:not(.browser-default),input[type=url]:not(.browser-default),input[type=time]:not(.browser-default),input[type=date]:not(.browser-default),input[type=datetime]:not(.browser-default),input[type=datetime-local]:not(.browser-default),input[type=tel]:not(.browser-default),input[type=number]:not(.browser-default),input[type=search]:not(.browser-default),textarea.materialize-textarea{background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;border-radius:0;outline:none;height:3rem;width:100%;font-size:16px;margin:0 0 8px 0;padding:0;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-transition:border .3s, -webkit-box-shadow .3s;transition:border .3s, -webkit-box-shadow .3s;transition:box-shadow .3s, border .3s;transition:box-shadow .3s, border .3s, -webkit-box-shadow .3s}input:not([type]):disabled,input:not([type])[readonly="readonly"],input[type=text]:not(.browser-default):disabled,input[type=text]:not(.browser-default)[readonly="readonly"],input[type=password]:not(.browser-default):disabled,input[type=password]:not(.browser-default)[readonly="readonly"],input[type=email]:not(.browser-default):disabled,input[type=email]:not(.browser-default)[readonly="readonly"],input[type=url]:not(.browser-default):disabled,input[type=url]:not(.browser-default)[readonly="readonly"],input[type=time]:not(.browser-default):disabled,input[type=time]:not(.browser-default)[readonly="readonly"],input[type=date]:not(.browser-default):disabled,input[type=date]:not(.browser-default)[readonly="readonly"],input[type=datetime]:not(.browser-default):disabled,input[type=datetime]:not(.browser-default)[readonly="readonly"],input[type=datetime-local]:not(.browser-default):disabled,input[type=datetime-local]:not(.browser-default)[readonly="readonly"],input[type=tel]:not(.browser-default):disabled,input[type=tel]:not(.browser-default)[readonly="readonly"],input[type=number]:not(.browser-default):disabled,input[type=number]:not(.browser-default)[readonly="readonly"],input[type=search]:not(.browser-default):disabled,input[type=search]:not(.browser-default)[readonly="readonly"],textarea.materialize-textarea:disabled,textarea.materialize-textarea[readonly="readonly"]{color:rgba(0,0,0,0.42);border-bottom:1px dotted rgba(0,0,0,0.42)}input:not([type]):disabled+label,input:not([type])[readonly="readonly"]+label,input[type=text]:not(.browser-default):disabled+label,input[type=text]:not(.browser-default)[readonly="readonly"]+label,input[type=password]:not(.browser-default):disabled+label,input[type=password]:not(.browser-default)[readonly="readonly"]+label,input[type=email]:not(.browser-default):disabled+label,input[type=email]:not(.browser-default)[readonly="readonly"]+label,input[type=url]:not(.browser-default):disabled+label,input[type=url]:not(.browser-default)[readonly="readonly"]+label,input[type=time]:not(.browser-default):disabled+label,input[type=time]:not(.browser-default)[readonly="readonly"]+label,input[type=date]:not(.browser-default):disabled+label,input[type=date]:not(.browser-default)[readonly="readonly"]+label,input[type=datetime]:not(.browser-default):disabled+label,input[type=datetime]:not(.browser-default)[readonly="readonly"]+label,input[type=datetime-local]:not(.browser-default):disabled+label,input[type=datetime-local]:not(.browser-default)[readonly="readonly"]+label,input[type=tel]:not(.browser-default):disabled+label,input[type=tel]:not(.browser-default)[readonly="readonly"]+label,input[type=number]:not(.browser-default):disabled+label,input[type=number]:not(.browser-default)[readonly="readonly"]+label,input[type=search]:not(.browser-default):disabled+label,input[type=search]:not(.browser-default)[readonly="readonly"]+label,textarea.materialize-textarea:disabled+label,textarea.materialize-textarea[readonly="readonly"]+label{color:rgba(0,0,0,0.42)}input:not([type]):focus:not([readonly]),input[type=text]:not(.browser-default):focus:not([readonly]),input[type=password]:not(.browser-default):focus:not([readonly]),input[type=email]:not(.browser-default):focus:not([readonly]),input[type=url]:not(.browser-default):focus:not([readonly]),input[type=time]:not(.browser-default):focus:not([readonly]),input[type=date]:not(.browser-default):focus:not([readonly]),input[type=datetime]:not(.browser-default):focus:not([readonly]),input[type=datetime-local]:not(.browser-default):focus:not([readonly]),input[type=tel]:not(.browser-default):focus:not([readonly]),input[type=number]:not(.browser-default):focus:not([readonly]),input[type=search]:not(.browser-default):focus:not([readonly]),textarea.materialize-textarea:focus:not([readonly]){border-bottom:1px solid #26a69a;-webkit-box-shadow:0 1px 0 0 #26a69a;box-shadow:0 1px 0 0 #26a69a}input:not([type]):focus:not([readonly])+label,input[type=text]:not(.browser-default):focus:not([readonly])+label,input[type=password]:not(.browser-default):focus:not([readonly])+label,input[type=email]:not(.browser-default):focus:not([readonly])+label,input[type=url]:not(.browser-default):focus:not([readonly])+label,input[type=time]:not(.browser-default):focus:not([readonly])+label,input[type=date]:not(.browser-default):focus:not([readonly])+label,input[type=datetime]:not(.browser-default):focus:not([readonly])+label,input[type=datetime-local]:not(.browser-default):focus:not([readonly])+label,input[type=tel]:not(.browser-default):focus:not([readonly])+label,input[type=number]:not(.browser-default):focus:not([readonly])+label,input[type=search]:not(.browser-default):focus:not([readonly])+label,textarea.materialize-textarea:focus:not([readonly])+label{color:#26a69a}input:not([type]):focus.valid ~ label,input[type=text]:not(.browser-default):focus.valid ~ label,input[type=password]:not(.browser-default):focus.valid ~ label,input[type=email]:not(.browser-default):focus.valid ~ label,input[type=url]:not(.browser-default):focus.valid ~ label,input[type=time]:not(.browser-default):focus.valid ~ label,input[type=date]:not(.browser-default):focus.valid ~ label,input[type=datetime]:not(.browser-default):focus.valid ~ label,input[type=datetime-local]:not(.browser-default):focus.valid ~ label,input[type=tel]:not(.browser-default):focus.valid ~ label,input[type=number]:not(.browser-default):focus.valid ~ label,input[type=search]:not(.browser-default):focus.valid ~ label,textarea.materialize-textarea:focus.valid ~ label{color:#4CAF50}input:not([type]):focus.invalid ~ label,input[type=text]:not(.browser-default):focus.invalid ~ label,input[type=password]:not(.browser-default):focus.invalid ~ label,input[type=email]:not(.browser-default):focus.invalid ~ label,input[type=url]:not(.browser-default):focus.invalid ~ label,input[type=time]:not(.browser-default):focus.invalid ~ label,input[type=date]:not(.browser-default):focus.invalid ~ label,input[type=datetime]:not(.browser-default):focus.invalid ~ label,input[type=datetime-local]:not(.browser-default):focus.invalid ~ label,input[type=tel]:not(.browser-default):focus.invalid ~ label,input[type=number]:not(.browser-default):focus.invalid ~ label,input[type=search]:not(.browser-default):focus.invalid ~ label,textarea.materialize-textarea:focus.invalid ~ label{color:#F44336}input:not([type]).validate+label,input[type=text]:not(.browser-default).validate+label,input[type=password]:not(.browser-default).validate+label,input[type=email]:not(.browser-default).validate+label,input[type=url]:not(.browser-default).validate+label,input[type=time]:not(.browser-default).validate+label,input[type=date]:not(.browser-default).validate+label,input[type=datetime]:not(.browser-default).validate+label,input[type=datetime-local]:not(.browser-default).validate+label,input[type=tel]:not(.browser-default).validate+label,input[type=number]:not(.browser-default).validate+label,input[type=search]:not(.browser-default).validate+label,textarea.materialize-textarea.validate+label{width:100%}input.valid:not([type]),input.valid:not([type]):focus,input.valid[type=text]:not(.browser-default),input.valid[type=text]:not(.browser-default):focus,input.valid[type=password]:not(.browser-default),input.valid[type=password]:not(.browser-default):focus,input.valid[type=email]:not(.browser-default),input.valid[type=email]:not(.browser-default):focus,input.valid[type=url]:not(.browser-default),input.valid[type=url]:not(.browser-default):focus,input.valid[type=time]:not(.browser-default),input.valid[type=time]:not(.browser-default):focus,input.valid[type=date]:not(.browser-default),input.valid[type=date]:not(.browser-default):focus,input.valid[type=datetime]:not(.browser-default),input.valid[type=datetime]:not(.browser-default):focus,input.valid[type=datetime-local]:not(.browser-default),input.valid[type=datetime-local]:not(.browser-default):focus,input.valid[type=tel]:not(.browser-default),input.valid[type=tel]:not(.browser-default):focus,input.valid[type=number]:not(.browser-default),input.valid[type=number]:not(.browser-default):focus,input.valid[type=search]:not(.browser-default),input.valid[type=search]:not(.browser-default):focus,textarea.materialize-textarea.valid,textarea.materialize-textarea.valid:focus,.select-wrapper.valid>input.select-dropdown{border-bottom:1px solid #4CAF50;-webkit-box-shadow:0 1px 0 0 #4CAF50;box-shadow:0 1px 0 0 #4CAF50}input.invalid:not([type]),input.invalid:not([type]):focus,input.invalid[type=text]:not(.browser-default),input.invalid[type=text]:not(.browser-default):focus,input.invalid[type=password]:not(.browser-default),input.invalid[type=password]:not(.browser-default):focus,input.invalid[type=email]:not(.browser-default),input.invalid[type=email]:not(.browser-default):focus,input.invalid[type=url]:not(.browser-default),input.invalid[type=url]:not(.browser-default):focus,input.invalid[type=time]:not(.browser-default),input.invalid[type=time]:not(.browser-default):focus,input.invalid[type=date]:not(.browser-default),input.invalid[type=date]:not(.browser-default):focus,input.invalid[type=datetime]:not(.browser-default),input.invalid[type=datetime]:not(.browser-default):focus,input.invalid[type=datetime-local]:not(.browser-default),input.invalid[type=datetime-local]:not(.browser-default):focus,input.invalid[type=tel]:not(.browser-default),input.invalid[type=tel]:not(.browser-default):focus,input.invalid[type=number]:not(.browser-default),input.invalid[type=number]:not(.browser-default):focus,input.invalid[type=search]:not(.browser-default),input.invalid[type=search]:not(.browser-default):focus,textarea.materialize-textarea.invalid,textarea.materialize-textarea.invalid:focus,.select-wrapper.invalid>input.select-dropdown,.select-wrapper.invalid>input.select-dropdown:focus{border-bottom:1px solid #F44336;-webkit-box-shadow:0 1px 0 0 #F44336;box-shadow:0 1px 0 0 #F44336}input:not([type]).valid ~ .helper-text[data-success],input:not([type]):focus.valid ~ .helper-text[data-success],input:not([type]).invalid ~ .helper-text[data-error],input:not([type]):focus.invalid ~ .helper-text[data-error],input[type=text]:not(.browser-default).valid ~ .helper-text[data-success],input[type=text]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=text]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=text]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=password]:not(.browser-default).valid ~ .helper-text[data-success],input[type=password]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=password]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=password]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=email]:not(.browser-default).valid ~ .helper-text[data-success],input[type=email]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=email]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=email]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=url]:not(.browser-default).valid ~ .helper-text[data-success],input[type=url]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=url]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=url]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=time]:not(.browser-default).valid ~ .helper-text[data-success],input[type=time]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=time]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=time]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=date]:not(.browser-default).valid ~ .helper-text[data-success],input[type=date]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=date]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=date]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=datetime]:not(.browser-default).valid ~ .helper-text[data-success],input[type=datetime]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=datetime]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=datetime]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=datetime-local]:not(.browser-default).valid ~ .helper-text[data-success],input[type=datetime-local]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=datetime-local]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=datetime-local]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=tel]:not(.browser-default).valid ~ .helper-text[data-success],input[type=tel]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=tel]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=tel]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=number]:not(.browser-default).valid ~ .helper-text[data-success],input[type=number]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=number]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=number]:not(.browser-default):focus.invalid ~ .helper-text[data-error],input[type=search]:not(.browser-default).valid ~ .helper-text[data-success],input[type=search]:not(.browser-default):focus.valid ~ .helper-text[data-success],input[type=search]:not(.browser-default).invalid ~ .helper-text[data-error],input[type=search]:not(.browser-default):focus.invalid ~ .helper-text[data-error],textarea.materialize-textarea.valid ~ .helper-text[data-success],textarea.materialize-textarea:focus.valid ~ .helper-text[data-success],textarea.materialize-textarea.invalid ~ .helper-text[data-error],textarea.materialize-textarea:focus.invalid ~ .helper-text[data-error],.select-wrapper.valid .helper-text[data-success],.select-wrapper.invalid ~ .helper-text[data-error]{color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}input:not([type]).valid ~ .helper-text:after,input:not([type]):focus.valid ~ .helper-text:after,input[type=text]:not(.browser-default).valid ~ .helper-text:after,input[type=text]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=password]:not(.browser-default).valid ~ .helper-text:after,input[type=password]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=email]:not(.browser-default).valid ~ .helper-text:after,input[type=email]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=url]:not(.browser-default).valid ~ .helper-text:after,input[type=url]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=time]:not(.browser-default).valid ~ .helper-text:after,input[type=time]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=date]:not(.browser-default).valid ~ .helper-text:after,input[type=date]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=datetime]:not(.browser-default).valid ~ .helper-text:after,input[type=datetime]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default).valid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=tel]:not(.browser-default).valid ~ .helper-text:after,input[type=tel]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=number]:not(.browser-default).valid ~ .helper-text:after,input[type=number]:not(.browser-default):focus.valid ~ .helper-text:after,input[type=search]:not(.browser-default).valid ~ .helper-text:after,input[type=search]:not(.browser-default):focus.valid ~ .helper-text:after,textarea.materialize-textarea.valid ~ .helper-text:after,textarea.materialize-textarea:focus.valid ~ .helper-text:after,.select-wrapper.valid ~ .helper-text:after{content:attr(data-success);color:#4CAF50}input:not([type]).invalid ~ .helper-text:after,input:not([type]):focus.invalid ~ .helper-text:after,input[type=text]:not(.browser-default).invalid ~ .helper-text:after,input[type=text]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=password]:not(.browser-default).invalid ~ .helper-text:after,input[type=password]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=email]:not(.browser-default).invalid ~ .helper-text:after,input[type=email]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=url]:not(.browser-default).invalid ~ .helper-text:after,input[type=url]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=time]:not(.browser-default).invalid ~ .helper-text:after,input[type=time]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=date]:not(.browser-default).invalid ~ .helper-text:after,input[type=date]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=datetime]:not(.browser-default).invalid ~ .helper-text:after,input[type=datetime]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default).invalid ~ .helper-text:after,input[type=datetime-local]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=tel]:not(.browser-default).invalid ~ .helper-text:after,input[type=tel]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=number]:not(.browser-default).invalid ~ .helper-text:after,input[type=number]:not(.browser-default):focus.invalid ~ .helper-text:after,input[type=search]:not(.browser-default).invalid ~ .helper-text:after,input[type=search]:not(.browser-default):focus.invalid ~ .helper-text:after,textarea.materialize-textarea.invalid ~ .helper-text:after,textarea.materialize-textarea:focus.invalid ~ .helper-text:after,.select-wrapper.invalid ~ .helper-text:after{content:attr(data-error);color:#F44336}input:not([type])+label:after,input[type=text]:not(.browser-default)+label:after,input[type=password]:not(.browser-default)+label:after,input[type=email]:not(.browser-default)+label:after,input[type=url]:not(.browser-default)+label:after,input[type=time]:not(.browser-default)+label:after,input[type=date]:not(.browser-default)+label:after,input[type=datetime]:not(.browser-default)+label:after,input[type=datetime-local]:not(.browser-default)+label:after,input[type=tel]:not(.browser-default)+label:after,input[type=number]:not(.browser-default)+label:after,input[type=search]:not(.browser-default)+label:after,textarea.materialize-textarea+label:after,.select-wrapper+label:after{display:block;content:"";position:absolute;top:100%;left:0;opacity:0;-webkit-transition:.2s opacity ease-out, .2s color ease-out;transition:.2s opacity ease-out, .2s color ease-out}.input-field{position:relative;margin-top:1rem;margin-bottom:1rem}.input-field.inline{display:inline-block;vertical-align:middle;margin-left:5px}.input-field.inline input,.input-field.inline .select-dropdown{margin-bottom:1rem}.input-field.col label{left:.75rem}.input-field.col .prefix ~ label,.input-field.col .prefix ~ .validate ~ label{width:calc(100% - 3rem - 1.5rem)}.input-field>label{color:#9e9e9e;position:absolute;top:0;left:0;font-size:1rem;cursor:text;-webkit-transition:color .2s ease-out, -webkit-transform .2s ease-out;transition:color .2s ease-out, -webkit-transform .2s ease-out;transition:transform .2s ease-out, color .2s ease-out;transition:transform .2s ease-out, color .2s ease-out, -webkit-transform .2s ease-out;-webkit-transform-origin:0% 100%;transform-origin:0% 100%;text-align:initial;-webkit-transform:translateY(12px);transform:translateY(12px)}.input-field>label:not(.label-icon).active{-webkit-transform:translateY(-14px) scale(0.8);transform:translateY(-14px) scale(0.8);-webkit-transform-origin:0 0;transform-origin:0 0}.input-field>input[type]:-webkit-autofill:not(.browser-default):not([type="search"])+label,.input-field>input[type=date]:not(.browser-default)+label,.input-field>input[type=time]:not(.browser-default)+label{-webkit-transform:translateY(-14px) scale(0.8);transform:translateY(-14px) scale(0.8);-webkit-transform-origin:0 0;transform-origin:0 0}.input-field .helper-text{position:relative;min-height:18px;display:block;font-size:12px;color:rgba(0,0,0,0.54)}.input-field .helper-text::after{opacity:1;position:absolute;top:0;left:0}.input-field .prefix{position:absolute;width:3rem;font-size:2rem;-webkit-transition:color .2s;transition:color .2s;top:.5rem}.input-field .prefix.active{color:#26a69a}.input-field .prefix ~ input,.input-field .prefix ~ textarea,.input-field .prefix ~ label,.input-field .prefix ~ .validate ~ label,.input-field .prefix ~ .helper-text,.input-field .prefix ~ .autocomplete-content{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.input-field .prefix ~ label{margin-left:3rem}@media only screen and (max-width: 992px){.input-field .prefix ~ input{width:86%;width:calc(100% - 3rem)}}@media only screen and (max-width: 600px){.input-field .prefix ~ input{width:80%;width:calc(100% - 3rem)}}.input-field input[type=search]{display:block;line-height:inherit;-webkit-transition:.3s background-color;transition:.3s background-color}.nav-wrapper .input-field input[type=search]{height:inherit;padding-left:4rem;width:calc(100% - 4rem);border:0;-webkit-box-shadow:none;box-shadow:none}.input-field input[type=search]:focus:not(.browser-default){background-color:#fff;border:0;-webkit-box-shadow:none;box-shadow:none;color:#444}.input-field input[type=search]:focus:not(.browser-default)+label i,.input-field input[type=search]:focus:not(.browser-default) ~ .mdi-navigation-close,.input-field input[type=search]:focus:not(.browser-default) ~ .material-icons{color:#444}.input-field input[type=search]+.label-icon{-webkit-transform:none;transform:none;left:1rem}.input-field input[type=search] ~ .mdi-navigation-close,.input-field input[type=search] ~ .material-icons{position:absolute;top:0;right:1rem;color:transparent;cursor:pointer;font-size:2rem;-webkit-transition:.3s color;transition:.3s color}textarea{width:100%;height:3rem;background-color:transparent}textarea.materialize-textarea{line-height:normal;overflow-y:hidden;padding:.8rem 0 .8rem 0;resize:none;min-height:3rem;-webkit-box-sizing:border-box;box-sizing:border-box}.hiddendiv{visibility:hidden;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;padding-top:1.2rem;position:absolute;top:0;z-index:-1}.autocomplete-content li .highlight{color:#444}.autocomplete-content li img{height:40px;width:40px;margin:5px 15px}.character-counter{min-height:18px}[type="radio"]:not(:checked),[type="radio"]:checked{position:absolute;opacity:0;pointer-events:none}[type="radio"]:not(:checked)+span,[type="radio"]:checked+span{position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-transition:.28s ease;transition:.28s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type="radio"]+span:before,[type="radio"]+span:after{content:'';position:absolute;left:0;top:0;margin:4px;width:16px;height:16px;z-index:0;-webkit-transition:.28s ease;transition:.28s ease}[type="radio"]:not(:checked)+span:before,[type="radio"]:not(:checked)+span:after,[type="radio"]:checked+span:before,[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:before,[type="radio"].with-gap:checked+span:after{border-radius:50%}[type="radio"]:not(:checked)+span:before,[type="radio"]:not(:checked)+span:after{border:2px solid #5a5a5a}[type="radio"]:not(:checked)+span:after{-webkit-transform:scale(0);transform:scale(0)}[type="radio"]:checked+span:before{border:2px solid transparent}[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:before,[type="radio"].with-gap:checked+span:after{border:2px solid #26a69a}[type="radio"]:checked+span:after,[type="radio"].with-gap:checked+span:after{background-color:#26a69a}[type="radio"]:checked+span:after{-webkit-transform:scale(1.02);transform:scale(1.02)}[type="radio"].with-gap:checked+span:after{-webkit-transform:scale(0.5);transform:scale(0.5)}[type="radio"].tabbed:focus+span:before{-webkit-box-shadow:0 0 0 10px rgba(0,0,0,0.1);box-shadow:0 0 0 10px rgba(0,0,0,0.1)}[type="radio"].with-gap:disabled:checked+span:before{border:2px solid rgba(0,0,0,0.42)}[type="radio"].with-gap:disabled:checked+span:after{border:none;background-color:rgba(0,0,0,0.42)}[type="radio"]:disabled:not(:checked)+span:before,[type="radio"]:disabled:checked+span:before{background-color:transparent;border-color:rgba(0,0,0,0.42)}[type="radio"]:disabled+span{color:rgba(0,0,0,0.42)}[type="radio"]:disabled:not(:checked)+span:before{border-color:rgba(0,0,0,0.42)}[type="radio"]:disabled:checked+span:after{background-color:rgba(0,0,0,0.42);border-color:#949494}[type="checkbox"]:not(:checked),[type="checkbox"]:checked{position:absolute;opacity:0;pointer-events:none}[type="checkbox"]+span:not(.lever){position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type="checkbox"]+span:not(.lever):before,[type="checkbox"]:not(.filled-in)+span:not(.lever):after{content:'';position:absolute;top:0;left:0;width:18px;height:18px;z-index:0;border:2px solid #5a5a5a;border-radius:1px;margin-top:3px;-webkit-transition:.2s;transition:.2s}[type="checkbox"]:not(.filled-in)+span:not(.lever):after{border:0;-webkit-transform:scale(0);transform:scale(0)}[type="checkbox"]:not(:checked):disabled+span:not(.lever):before{border:none;background-color:rgba(0,0,0,0.42)}[type="checkbox"].tabbed:focus+span:not(.lever):after{-webkit-transform:scale(1);transform:scale(1);border:0;border-radius:50%;-webkit-box-shadow:0 0 0 10px rgba(0,0,0,0.1);box-shadow:0 0 0 10px rgba(0,0,0,0.1);background-color:rgba(0,0,0,0.1)}[type="checkbox"]:checked+span:not(.lever):before{top:-4px;left:-5px;width:12px;height:22px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #26a69a;border-bottom:2px solid #26a69a;-webkit-transform:rotate(40deg);transform:rotate(40deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:checked:disabled+span:before{border-right:2px solid rgba(0,0,0,0.42);border-bottom:2px solid rgba(0,0,0,0.42)}[type="checkbox"]:indeterminate+span:not(.lever):before{top:-11px;left:-12px;width:10px;height:22px;border-top:none;border-left:none;border-right:2px solid #26a69a;border-bottom:none;-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:indeterminate:disabled+span:not(.lever):before{border-right:2px solid rgba(0,0,0,0.42);background-color:transparent}[type="checkbox"].filled-in+span:not(.lever):after{border-radius:2px}[type="checkbox"].filled-in+span:not(.lever):before,[type="checkbox"].filled-in+span:not(.lever):after{content:'';left:0;position:absolute;-webkit-transition:border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;transition:border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;z-index:1}[type="checkbox"].filled-in:not(:checked)+span:not(.lever):before{width:0;height:0;border:3px solid transparent;left:6px;top:10px;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"].filled-in:not(:checked)+span:not(.lever):after{height:20px;width:20px;background-color:transparent;border:2px solid #5a5a5a;top:0px;z-index:0}[type="checkbox"].filled-in:checked+span:not(.lever):before{top:0;left:1px;width:8px;height:13px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #fff;border-bottom:2px solid #fff;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"].filled-in:checked+span:not(.lever):after{top:0;width:20px;height:20px;border:2px solid #26a69a;background-color:#26a69a;z-index:0}[type="checkbox"].filled-in.tabbed:focus+span:not(.lever):after{border-radius:2px;border-color:#5a5a5a;background-color:rgba(0,0,0,0.1)}[type="checkbox"].filled-in.tabbed:checked:focus+span:not(.lever):after{border-radius:2px;background-color:#26a69a;border-color:#26a69a}[type="checkbox"].filled-in:disabled:not(:checked)+span:not(.lever):before{background-color:transparent;border:2px solid transparent}[type="checkbox"].filled-in:disabled:not(:checked)+span:not(.lever):after{border-color:transparent;background-color:#949494}[type="checkbox"].filled-in:disabled:checked+span:not(.lever):before{background-color:transparent}[type="checkbox"].filled-in:disabled:checked+span:not(.lever):after{background-color:#949494;border-color:#949494}.switch,.switch *{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch label{cursor:pointer}.switch label input[type=checkbox]{opacity:0;width:0;height:0}.switch label input[type=checkbox]:checked+.lever{background-color:#84c7c1}.switch label input[type=checkbox]:checked+.lever:before,.switch label input[type=checkbox]:checked+.lever:after{left:18px}.switch label input[type=checkbox]:checked+.lever:after{background-color:#26a69a}.switch label .lever{content:"";display:inline-block;position:relative;width:36px;height:14px;background-color:rgba(0,0,0,0.38);border-radius:15px;margin-right:10px;-webkit-transition:background 0.3s ease;transition:background 0.3s ease;vertical-align:middle;margin:0 16px}.switch label .lever:before,.switch label .lever:after{content:"";position:absolute;display:inline-block;width:20px;height:20px;border-radius:50%;left:0;top:-3px;-webkit-transition:left 0.3s ease, background .3s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease;transition:left 0.3s ease, background .3s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease;transition:left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease;transition:left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease, -webkit-box-shadow 0.1s ease, -webkit-transform .1s ease}.switch label .lever:before{background-color:rgba(38,166,154,0.15)}.switch label .lever:after{background-color:#F1F1F1;-webkit-box-shadow:0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12);box-shadow:0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12)}input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before,input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before{-webkit-transform:scale(2.4);transform:scale(2.4);background-color:rgba(38,166,154,0.15)}input[type=checkbox]:not(:disabled) ~ .lever:active:before,input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before{-webkit-transform:scale(2.4);transform:scale(2.4);background-color:rgba(0,0,0,0.08)}.switch input[type=checkbox][disabled]+.lever{cursor:default;background-color:rgba(0,0,0,0.12)}.switch label input[type=checkbox][disabled]+.lever:after,.switch label input[type=checkbox][disabled]:checked+.lever:after{background-color:#949494}select{display:none}select.browser-default{display:block}select{background-color:rgba(255,255,255,0.9);width:100%;padding:5px;border:1px solid #f2f2f2;border-radius:2px;height:3rem}.select-label{position:absolute}.select-wrapper{position:relative}.select-wrapper.valid+label,.select-wrapper.invalid+label{width:100%;pointer-events:none}.select-wrapper input.select-dropdown{position:relative;cursor:pointer;background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;outline:none;height:3rem;line-height:3rem;width:100%;font-size:16px;margin:0 0 8px 0;padding:0;display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:1}.select-wrapper input.select-dropdown:focus{border-bottom:1px solid #26a69a}.select-wrapper .caret{position:absolute;right:0;top:0;bottom:0;margin:auto 0;z-index:0;fill:rgba(0,0,0,0.87)}.select-wrapper+label{position:absolute;top:-26px;font-size:.8rem}select:disabled{color:rgba(0,0,0,0.42)}.select-wrapper.disabled+label{color:rgba(0,0,0,0.42)}.select-wrapper.disabled .caret{fill:rgba(0,0,0,0.42)}.select-wrapper input.select-dropdown:disabled{color:rgba(0,0,0,0.42);cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.select-wrapper i{color:rgba(0,0,0,0.3)}.select-dropdown li.disabled,.select-dropdown li.disabled>span,.select-dropdown li.optgroup{color:rgba(0,0,0,0.3);background-color:transparent}body.keyboard-focused .select-dropdown.dropdown-content li:focus{background-color:rgba(0,0,0,0.08)}.select-dropdown.dropdown-content li:hover{background-color:rgba(0,0,0,0.08)}.select-dropdown.dropdown-content li.selected{background-color:rgba(0,0,0,0.03)}.prefix ~ .select-wrapper{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.prefix ~ label{margin-left:3rem}.select-dropdown li img{height:40px;width:40px;margin:5px 15px;float:right}.select-dropdown li.optgroup{border-top:1px solid #eee}.select-dropdown li.optgroup.selected>span{color:rgba(0,0,0,0.7)}.select-dropdown li.optgroup>span{color:rgba(0,0,0,0.4)}.select-dropdown li.optgroup ~ li.optgroup-option{padding-left:1rem}.file-field{position:relative}.file-field .file-path-wrapper{overflow:hidden;padding-left:10px}.file-field input.file-path{width:100%}.file-field .btn,.file-field .btn-large,.file-field .btn-small{float:left;height:3rem;line-height:3rem}.file-field span{cursor:pointer}.file-field input[type=file]{position:absolute;top:0;right:0;left:0;bottom:0;width:100%;margin:0;padding:0;font-size:20px;cursor:pointer;opacity:0;filter:alpha(opacity=0)}.file-field input[type=file]::-webkit-file-upload-button{display:none}.range-field{position:relative}input[type=range],input[type=range]+.thumb{cursor:pointer}input[type=range]{position:relative;background-color:transparent;border:none;outline:none;width:100%;margin:15px 0;padding:0}input[type=range]:focus{outline:none}input[type=range]+.thumb{position:absolute;top:10px;left:0;border:none;height:0;width:0;border-radius:50%;background-color:#26a69a;margin-left:7px;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}input[type=range]+.thumb .value{display:block;width:30px;text-align:center;color:#26a69a;font-size:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}input[type=range]+.thumb.active{border-radius:50% 50% 50% 0}input[type=range]+.thumb.active .value{color:#fff;margin-left:-1px;margin-top:8px;font-size:10px}input[type=range]{-webkit-appearance:none}input[type=range]::-webkit-slider-runnable-track{height:3px;background:#c2c0c2;border:none}input[type=range]::-webkit-slider-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s;-webkit-appearance:none;background-color:#26a69a;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;margin:-5px 0 0 0}.keyboard-focused input[type=range]:focus:not(.active)::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 10px rgba(38,166,154,0.26);box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range]{border:1px solid white}input[type=range]::-moz-range-track{height:3px;background:#c2c0c2;border:none}input[type=range]::-moz-focus-inner{border:0}input[type=range]::-moz-range-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s;margin-top:-5px}input[type=range]:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}.keyboard-focused input[type=range]:focus:not(.active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range]::-ms-track{height:3px;background:transparent;border-color:transparent;border-width:6px 0;color:transparent}input[type=range]::-ms-fill-lower{background:#777}input[type=range]::-ms-fill-upper{background:#ddd}input[type=range]::-ms-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;-webkit-transition:-webkit-box-shadow .3s;transition:-webkit-box-shadow .3s;transition:box-shadow .3s;transition:box-shadow .3s, -webkit-box-shadow .3s}.keyboard-focused input[type=range]:focus:not(.active)::-ms-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)}.table-of-contents.fixed{position:fixed}.table-of-contents li{padding:2px 0}.table-of-contents a{display:inline-block;font-weight:300;color:#757575;padding-left:16px;height:1.5rem;line-height:1.5rem;letter-spacing:.4;display:inline-block}.table-of-contents a:hover{color:#a8a8a8;padding-left:15px;border-left:1px solid #ee6e73}.table-of-contents a.active{font-weight:500;padding-left:14px;border-left:2px solid #ee6e73}.sidenav{position:fixed;width:300px;left:0;top:0;margin:0;-webkit-transform:translateX(-100%);transform:translateX(-100%);height:100%;height:calc(100% + 60px);height:-moz-calc(100%);padding-bottom:60px;background-color:#fff;z-index:999;overflow-y:auto;will-change:transform;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateX(-105%);transform:translateX(-105%)}.sidenav.right-aligned{right:0;-webkit-transform:translateX(105%);transform:translateX(105%);left:auto;-webkit-transform:translateX(100%);transform:translateX(100%)}.sidenav .collapsible{margin:0}.sidenav li{float:none;line-height:48px}.sidenav li.active{background-color:rgba(0,0,0,0.05)}.sidenav li>a{color:rgba(0,0,0,0.87);display:block;font-size:14px;font-weight:500;height:48px;line-height:48px;padding:0 32px}.sidenav li>a:hover{background-color:rgba(0,0,0,0.05)}.sidenav li>a.btn,.sidenav li>a.btn-large,.sidenav li>a.btn-small,.sidenav li>a.btn-large,.sidenav li>a.btn-flat,.sidenav li>a.btn-floating{margin:10px 15px}.sidenav li>a.btn,.sidenav li>a.btn-large,.sidenav li>a.btn-small,.sidenav li>a.btn-large,.sidenav li>a.btn-floating{color:#fff}.sidenav li>a.btn-flat{color:#343434}.sidenav li>a.btn:hover,.sidenav li>a.btn-large:hover,.sidenav li>a.btn-small:hover,.sidenav li>a.btn-large:hover{background-color:#2bbbad}.sidenav li>a.btn-floating:hover{background-color:#26a69a}.sidenav li>a>i,.sidenav li>a>[class^="mdi-"],.sidenav li>a li>a>[class*="mdi-"],.sidenav li>a>i.material-icons{float:left;height:48px;line-height:48px;margin:0 32px 0 0;width:24px;color:rgba(0,0,0,0.54)}.sidenav .divider{margin:8px 0 0 0}.sidenav .subheader{cursor:initial;pointer-events:none;color:rgba(0,0,0,0.54);font-size:14px;font-weight:500;line-height:48px}.sidenav .subheader:hover{background-color:transparent}.sidenav .user-view{position:relative;padding:32px 32px 0;margin-bottom:8px}.sidenav .user-view>a{height:auto;padding:0}.sidenav .user-view>a:hover{background-color:transparent}.sidenav .user-view .background{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1}.sidenav .user-view .circle,.sidenav .user-view .name,.sidenav .user-view .email{display:block}.sidenav .user-view .circle{height:64px;width:64px}.sidenav .user-view .name,.sidenav .user-view .email{font-size:14px;line-height:24px}.sidenav .user-view .name{margin-top:16px;font-weight:500}.sidenav .user-view .email{padding-bottom:16px;font-weight:400}.drag-target{height:100%;width:10px;position:fixed;top:0;z-index:998}.drag-target.right-aligned{right:0}.sidenav.sidenav-fixed{left:0;-webkit-transform:translateX(0);transform:translateX(0);position:fixed}.sidenav.sidenav-fixed.right-aligned{right:0;left:auto}@media only screen and (max-width: 992px){.sidenav.sidenav-fixed{-webkit-transform:translateX(-105%);transform:translateX(-105%)}.sidenav.sidenav-fixed.right-aligned{-webkit-transform:translateX(105%);transform:translateX(105%)}.sidenav>a{padding:0 16px}.sidenav .user-view{padding:16px 16px 0}}.sidenav .collapsible-body>ul:not(.collapsible)>li.active,.sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active{background-color:#ee6e73}.sidenav .collapsible-body>ul:not(.collapsible)>li.active a,.sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active a{color:#fff}.sidenav .collapsible-body{padding:0}.sidenav-overlay{position:fixed;top:0;left:0;right:0;opacity:0;height:120vh;background-color:rgba(0,0,0,0.5);z-index:997;display:none}.preloader-wrapper{display:inline-block;position:relative;width:50px;height:50px}.preloader-wrapper.small{width:36px;height:36px}.preloader-wrapper.big{width:64px;height:64px}.preloader-wrapper.active{-webkit-animation:container-rotate 1568ms linear infinite;animation:container-rotate 1568ms linear infinite}@-webkit-keyframes container-rotate{to{-webkit-transform:rotate(360deg)}}@keyframes container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-layer{position:absolute;width:100%;height:100%;opacity:0;border-color:#26a69a}.spinner-blue,.spinner-blue-only{border-color:#4285f4}.spinner-red,.spinner-red-only{border-color:#db4437}.spinner-yellow,.spinner-yellow-only{border-color:#f4b400}.spinner-green,.spinner-green-only{border-color:#0f9d58}.active .spinner-layer.spinner-blue{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-red{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-yellow{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-green{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer,.active .spinner-layer.spinner-blue-only,.active .spinner-layer.spinner-red-only,.active .spinner-layer.spinner-yellow-only,.active .spinner-layer.spinner-green-only{opacity:1;-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg)}}@keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@-webkit-keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@-webkit-keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@-webkit-keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}@keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}.gap-patch{position:absolute;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.gap-patch .circle{width:1000%;left:-450%}.circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.circle-clipper .circle{width:200%;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent !important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0}.circle-clipper.left .circle{left:0;border-right-color:transparent !important;-webkit-transform:rotate(129deg);transform:rotate(129deg)}.circle-clipper.right .circle{left:-100%;border-left-color:transparent !important;-webkit-transform:rotate(-129deg);transform:rotate(-129deg)}.active .circle-clipper.left .circle{-webkit-animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .circle-clipper.right .circle{-webkit-animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes left-spin{from{-webkit-transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg)}}@keyframes left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes right-spin{from{-webkit-transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg)}}@keyframes right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}#spinnerContainer.cooldown{-webkit-animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1);animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1)}@-webkit-keyframes fade-out{from{opacity:1}to{opacity:0}}@keyframes fade-out{from{opacity:1}to{opacity:0}}.slider{position:relative;height:400px;width:100%}.slider.fullscreen{height:100%;width:100%;position:absolute;top:0;left:0;right:0;bottom:0}.slider.fullscreen ul.slides{height:100%}.slider.fullscreen ul.indicators{z-index:2;bottom:30px}.slider .slides{background-color:#9e9e9e;margin:0;height:400px}.slider .slides li{opacity:0;position:absolute;top:0;left:0;z-index:1;width:100%;height:inherit;overflow:hidden}.slider .slides li img{height:100%;width:100%;background-size:cover;background-position:center}.slider .slides li .caption{color:#fff;position:absolute;top:15%;left:15%;width:70%;opacity:0}.slider .slides li .caption p{color:#e0e0e0}.slider .slides li.active{z-index:2}.slider .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.slider .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:16px;width:16px;margin:0 12px;background-color:#e0e0e0;-webkit-transition:background-color .3s;transition:background-color .3s;border-radius:50%}.slider .indicators .indicator-item.active{background-color:#4CAF50}.carousel{overflow:hidden;position:relative;width:100%;height:400px;-webkit-perspective:500px;perspective:500px;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform-origin:0% 50%;transform-origin:0% 50%}.carousel.carousel-slider{top:0;left:0}.carousel.carousel-slider .carousel-fixed-item{position:absolute;left:0;right:0;bottom:20px;z-index:1}.carousel.carousel-slider .carousel-fixed-item.with-indicators{bottom:68px}.carousel.carousel-slider .carousel-item{width:100%;height:100%;min-height:400px;position:absolute;top:0;left:0}.carousel.carousel-slider .carousel-item h2{font-size:24px;font-weight:500;line-height:32px}.carousel.carousel-slider .carousel-item p{font-size:15px}.carousel .carousel-item{visibility:hidden;width:200px;height:200px;position:absolute;top:0;left:0}.carousel .carousel-item>img{width:100%}.carousel .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.carousel .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:8px;width:8px;margin:24px 4px;background-color:rgba(255,255,255,0.5);-webkit-transition:background-color .3s;transition:background-color .3s;border-radius:50%}.carousel .indicators .indicator-item.active{background-color:#fff}.carousel.scrolling .carousel-item .materialboxed,.carousel .carousel-item:not(.active) .materialboxed{pointer-events:none}.tap-target-wrapper{width:800px;height:800px;position:fixed;z-index:1000;visibility:hidden;-webkit-transition:visibility 0s .3s;transition:visibility 0s .3s}.tap-target-wrapper.open{visibility:visible;-webkit-transition:visibility 0s;transition:visibility 0s}.tap-target-wrapper.open .tap-target{-webkit-transform:scale(1);transform:scale(1);opacity:.95;-webkit-transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1)}.tap-target-wrapper.open .tap-target-wave::before{-webkit-transform:scale(1);transform:scale(1)}.tap-target-wrapper.open .tap-target-wave::after{visibility:visible;-webkit-animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;-webkit-transition:opacity .3s, visibility 0s 1s, -webkit-transform .3s;transition:opacity .3s, visibility 0s 1s, -webkit-transform .3s;transition:opacity .3s, transform .3s, visibility 0s 1s;transition:opacity .3s, transform .3s, visibility 0s 1s, -webkit-transform .3s}.tap-target{position:absolute;font-size:1rem;border-radius:50%;background-color:#ee6e73;-webkit-box-shadow:0 20px 20px 0 rgba(0,0,0,0.14),0 10px 50px 0 rgba(0,0,0,0.12),0 30px 10px -20px rgba(0,0,0,0.2);box-shadow:0 20px 20px 0 rgba(0,0,0,0.14),0 10px 50px 0 rgba(0,0,0,0.12),0 30px 10px -20px rgba(0,0,0,0.2);width:100%;height:100%;opacity:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1);transition:transform 0.3s cubic-bezier(0.42, 0, 0.58, 1),opacity 0.3s cubic-bezier(0.42, 0, 0.58, 1),-webkit-transform 0.3s cubic-bezier(0.42, 0, 0.58, 1)}.tap-target-content{position:relative;display:table-cell}.tap-target-wave{position:absolute;border-radius:50%;z-index:10001}.tap-target-wave::before,.tap-target-wave::after{content:'';display:block;position:absolute;width:100%;height:100%;border-radius:50%;background-color:#ffffff}.tap-target-wave::before{-webkit-transform:scale(0);transform:scale(0);-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s, -webkit-transform .3s}.tap-target-wave::after{visibility:hidden;-webkit-transition:opacity .3s, visibility 0s, -webkit-transform .3s;transition:opacity .3s, visibility 0s, -webkit-transform .3s;transition:opacity .3s, transform .3s, visibility 0s;transition:opacity .3s, transform .3s, visibility 0s, -webkit-transform .3s;z-index:-1}.tap-target-origin{top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);z-index:10002;position:absolute !important}.tap-target-origin:not(.btn):not(.btn-large):not(.btn-small),.tap-target-origin:not(.btn):not(.btn-large):not(.btn-small):hover{background:none}@media only screen and (max-width: 600px){.tap-target,.tap-target-wrapper{width:600px;height:600px}}.pulse{overflow:visible;position:relative}.pulse::before{content:'';display:block;position:absolute;width:100%;height:100%;top:0;left:0;background-color:inherit;border-radius:inherit;-webkit-transition:opacity .3s, -webkit-transform .3s;transition:opacity .3s, -webkit-transform .3s;transition:opacity .3s, transform .3s;transition:opacity .3s, transform .3s, -webkit-transform .3s;-webkit-animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;animation:pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;z-index:-1}@-webkit-keyframes pulse-animation{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}50%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}}@keyframes pulse-animation{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}50%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}}.datepicker-modal{max-width:325px;min-width:300px;max-height:none}.datepicker-container.modal-content{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding:0}.datepicker-controls{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;width:280px;margin:0 auto}.datepicker-controls .selects-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.datepicker-controls .select-wrapper input{border-bottom:none;text-align:center;margin:0}.datepicker-controls .select-wrapper input:focus{border-bottom:none}.datepicker-controls .select-wrapper .caret{display:none}.datepicker-controls .select-year input{width:50px}.datepicker-controls .select-month input{width:70px}.month-prev,.month-next{margin-top:4px;cursor:pointer;background-color:transparent;border:none}.datepicker-date-display{-webkit-box-flex:1;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;background-color:#26a69a;color:#fff;padding:20px 22px;font-weight:500}.datepicker-date-display .year-text{display:block;font-size:1.5rem;line-height:25px;color:rgba(255,255,255,0.7)}.datepicker-date-display .date-text{display:block;font-size:2.8rem;line-height:47px;font-weight:500}.datepicker-calendar-container{-webkit-box-flex:2.5;-webkit-flex:2.5 auto;-ms-flex:2.5 auto;flex:2.5 auto}.datepicker-table{width:280px;font-size:1rem;margin:0 auto}.datepicker-table thead{border-bottom:none}.datepicker-table th{padding:10px 5px;text-align:center}.datepicker-table tr{border:none}.datepicker-table abbr{text-decoration:none;color:#999}.datepicker-table td{border-radius:50%;padding:0}.datepicker-table td.is-today{color:#26a69a}.datepicker-table td.is-selected{background-color:#26a69a;color:#fff}.datepicker-table td.is-outside-current-month,.datepicker-table td.is-disabled{color:rgba(0,0,0,0.3);pointer-events:none}.datepicker-day-button{background-color:transparent;border:none;line-height:38px;display:block;width:100%;border-radius:50%;padding:0 5px;cursor:pointer;color:inherit}.datepicker-day-button:focus{background-color:rgba(43,161,150,0.25)}.datepicker-footer{width:280px;margin:0 auto;padding-bottom:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.datepicker-cancel,.datepicker-clear,.datepicker-today,.datepicker-done{color:#26a69a;padding:0 1rem}.datepicker-clear{color:#F44336}@media only screen and (min-width: 601px){.datepicker-modal{max-width:625px}.datepicker-container.modal-content{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.datepicker-date-display{-webkit-box-flex:0;-webkit-flex:0 1 270px;-ms-flex:0 1 270px;flex:0 1 270px}.datepicker-controls,.datepicker-table,.datepicker-footer{width:320px}.datepicker-day-button{line-height:44px}}.timepicker-modal{max-width:325px;max-height:none}.timepicker-container.modal-content{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding:0}.text-primary{color:#fff}.timepicker-digital-display{-webkit-box-flex:1;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;background-color:#26a69a;padding:10px;font-weight:300}.timepicker-text-container{font-size:4rem;font-weight:bold;text-align:center;color:rgba(255,255,255,0.6);font-weight:400;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timepicker-span-hours,.timepicker-span-minutes,.timepicker-span-am-pm div{cursor:pointer}.timepicker-span-hours{margin-right:3px}.timepicker-span-minutes{margin-left:3px}.timepicker-display-am-pm{font-size:1.3rem;position:absolute;right:1rem;bottom:1rem;font-weight:400}.timepicker-analog-display{-webkit-box-flex:2.5;-webkit-flex:2.5 auto;-ms-flex:2.5 auto;flex:2.5 auto}.timepicker-plate{background-color:#eee;border-radius:50%;width:270px;height:270px;overflow:visible;position:relative;margin:auto;margin-top:25px;margin-bottom:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timepicker-canvas,.timepicker-dial{position:absolute;left:0;right:0;top:0;bottom:0}.timepicker-minutes{visibility:hidden}.timepicker-tick{border-radius:50%;color:rgba(0,0,0,0.87);line-height:40px;text-align:center;width:40px;height:40px;position:absolute;cursor:pointer;font-size:15px}.timepicker-tick.active,.timepicker-tick:hover{background-color:rgba(38,166,154,0.25)}.timepicker-dial{-webkit-transition:opacity 350ms, -webkit-transform 350ms;transition:opacity 350ms, -webkit-transform 350ms;transition:transform 350ms, opacity 350ms;transition:transform 350ms, opacity 350ms, -webkit-transform 350ms}.timepicker-dial-out{opacity:0}.timepicker-dial-out.timepicker-hours{-webkit-transform:scale(1.1, 1.1);transform:scale(1.1, 1.1)}.timepicker-dial-out.timepicker-minutes{-webkit-transform:scale(0.8, 0.8);transform:scale(0.8, 0.8)}.timepicker-canvas{-webkit-transition:opacity 175ms;transition:opacity 175ms}.timepicker-canvas line{stroke:#26a69a;stroke-width:4;stroke-linecap:round}.timepicker-canvas-out{opacity:0.25}.timepicker-canvas-bearing{stroke:none;fill:#26a69a}.timepicker-canvas-bg{stroke:none;fill:#26a69a}.timepicker-footer{margin:0 auto;padding:5px 1rem;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.timepicker-clear{color:#F44336}.timepicker-close{color:#26a69a}.timepicker-clear,.timepicker-close{padding:0 20px}@media only screen and (min-width: 601px){.timepicker-modal{max-width:600px}.timepicker-container.modal-content{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.timepicker-text-container{top:32%}.timepicker-display-am-pm{position:relative;right:auto;bottom:auto;text-align:center;margin-top:1.2rem}} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..cb726a7 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/fullcalendar/locales-all.js b/static/fullcalendar/locales-all.js new file mode 100644 index 0000000..4615c7e --- /dev/null +++ b/static/fullcalendar/locales-all.js @@ -0,0 +1,1423 @@ +[].push.apply(FullCalendar.globalLocales, function () { + 'use strict'; + + var l0 = { + code: "af", + week: { + dow: 1, // Maandag is die eerste dag van die week. + doy: 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. + }, + buttonText: { + prev: "Vorige", + next: "Volgende", + today: "Vandag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Heeldag", + moreLinkText: "Addisionele", + noEventsText: "Daar is geen gebeurtenisse nie" + }; + + var l1 = { + code: "ar-dz", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 4 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l2 = { + code: "ar-kw", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l3 = { + code: "ar-ly", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l4 = { + code: "ar-ma", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l5 = { + code: "ar-sa", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l6 = { + code: "ar-tn", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l7 = { + code: "ar", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + var l8 = { + code: "az", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Əvvəl", + next: "Sonra", + today: "Bu Gün", + month: "Ay", + week: "Həftə", + day: "Gün", + list: "Gündəm" + }, + weekText: "Həftə", + allDayText: "Bütün Gün", + moreLinkText: function(n) { + return "+ daha çox " + n; + }, + noEventsText: "Göstərmək üçün hadisə yoxdur" + }; + + var l9 = { + code: "bg", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "назад", + next: "напред", + today: "днес", + month: "Месец", + week: "Седмица", + day: "Ден", + list: "График" + }, + allDayText: "Цял ден", + moreLinkText: function(n) { + return "+още " + n; + }, + noEventsText: "Няма събития за показване" + }; + + var l10 = { + code: "bs", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prošli", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Sedmica", + day: "Dan", + list: "Raspored" + }, + weekText: "Sed", + allDayText: "Cijeli dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nema događaja za prikazivanje" + }; + + var l11 = { + code: "ca", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Següent", + today: "Avui", + month: "Mes", + week: "Setmana", + day: "Dia", + list: "Agenda" + }, + weekText: "Set", + allDayText: "Tot el dia", + moreLinkText: "més", + noEventsText: "No hi ha esdeveniments per mostrar" + }; + + var l12 = { + code: "cs", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Dříve", + next: "Později", + today: "Nyní", + month: "Měsíc", + week: "Týden", + day: "Den", + list: "Agenda" + }, + weekText: "Týd", + allDayText: "Celý den", + moreLinkText: function(n) { + return "+další: " + n; + }, + noEventsText: "Žádné akce k zobrazení" + }; + + var l13 = { + code: "da", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Næste", + today: "I dag", + month: "Måned", + week: "Uge", + day: "Dag", + list: "Agenda" + }, + weekText: "Uge", + allDayText: "Hele dagen", + moreLinkText: "flere", + noEventsText: "Ingen arrangementer at vise" + }; + + var l14 = { + code: "de", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zurück", + next: "Vor", + today: "Heute", + year: "Jahr", + month: "Monat", + week: "Woche", + day: "Tag", + list: "Terminübersicht" + }, + weekText: "KW", + allDayText: "Ganztägig", + moreLinkText: function(n) { + return "+ weitere " + n; + }, + noEventsText: "Keine Ereignisse anzuzeigen" + }; + + var l15 = { + code: "el", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4st is the first week of the year. + }, + buttonText: { + prev: "Προηγούμενος", + next: "Επόμενος", + today: "Σήμερα", + month: "Μήνας", + week: "Εβδομάδα", + day: "Ημέρα", + list: "Ατζέντα" + }, + weekText: "Εβδ", + allDayText: "Ολοήμερο", + moreLinkText: "περισσότερα", + noEventsText: "Δεν υπάρχουν γεγονότα προς εμφάνιση" + }; + + var l16 = { + code: "en-au", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var l17 = { + code: "en-gb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var l18 = { + code: "en-nz", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var l19 = { + code: "es", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Todo el día", + moreLinkText: "más", + noEventsText: "No hay eventos para mostrar" + }; + + var l20 = { + code: "es", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Todo el día", + moreLinkText: "más", + noEventsText: "No hay eventos para mostrar" + }; + + var l21 = { + code: "et", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Eelnev", + next: "Järgnev", + today: "Täna", + month: "Kuu", + week: "Nädal", + day: "Päev", + list: "Päevakord" + }, + weekText: "näd", + allDayText: "Kogu päev", + moreLinkText: function(n) { + return "+ veel " + n; + }, + noEventsText: "Kuvamiseks puuduvad sündmused" + }; + + var l22 = { + code: "eu", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Aur", + next: "Hur", + today: "Gaur", + month: "Hilabetea", + week: "Astea", + day: "Eguna", + list: "Agenda" + }, + weekText: "As", + allDayText: "Egun osoa", + moreLinkText: "gehiago", + noEventsText: "Ez dago ekitaldirik erakusteko" + }; + + var l23 = { + code: "fa", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "قبلی", + next: "بعدی", + today: "امروز", + month: "ماه", + week: "هفته", + day: "روز", + list: "برنامه" + }, + weekText: "هف", + allDayText: "تمام روز", + moreLinkText: function(n) { + return "بیش از " + n; + }, + noEventsText: "هیچ رویدادی به نمایش" + }; + + var l24 = { + code: "fi", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Edellinen", + next: "Seuraava", + today: "Tänään", + month: "Kuukausi", + week: "Viikko", + day: "Päivä", + list: "Tapahtumat" + }, + weekText: "Vk", + allDayText: "Koko päivä", + moreLinkText: "lisää", + noEventsText: "Ei näytettäviä tapahtumia" + }; + + var l25 = { + code: "fr", + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekText: "Sem.", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + var l26 = { + code: "fr-ch", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Courant", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekText: "Sm", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + var l27 = { + code: "fr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Planning" + }, + weekText: "Sem.", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + var l28 = { + code: "gl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Seg", + today: "Hoxe", + month: "Mes", + week: "Semana", + day: "Día", + list: "Axenda" + }, + weekText: "Sm", + allDayText: "Todo o día", + moreLinkText: "máis", + noEventsText: "Non hai eventos para amosar" + }; + + var l29 = { + code: "he", + direction: 'rtl', + buttonText: { + prev: "הקודם", + next: "הבא", + today: "היום", + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + moreLinkText: "אחר", + noEventsText: "אין אירועים להצגה", + weekText: "שבוע" + }; + + var l30 = { + code: "hi", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "पिछला", + next: "अगला", + today: "आज", + month: "महीना", + week: "सप्ताह", + day: "दिन", + list: "कार्यसूची" + }, + weekText: "हफ्ता", + allDayText: "सभी दिन", + moreLinkText: function(n) { + return "+अधिक " + n; + }, + noEventsText: "कोई घटनाओं को प्रदर्शित करने के लिए" + }; + + var l31 = { + code: "hr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prijašnji", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Tjedan", + day: "Dan", + list: "Raspored" + }, + weekText: "Tje", + allDayText: "Cijeli dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nema događaja za prikaz" + }; + + var l32 = { + code: "hu", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "vissza", + next: "előre", + today: "ma", + month: "Hónap", + week: "Hét", + day: "Nap", + list: "Napló" + }, + weekText: "Hét", + allDayText: "Egész nap", + moreLinkText: "további", + noEventsText: "Nincs megjeleníthető esemény" + }; + + var l33 = { + code: "id", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "mundur", + next: "maju", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekText: "Mg", + allDayText: "Sehari penuh", + moreLinkText: "lebih", + noEventsText: "Tidak ada acara untuk ditampilkan" + }; + + var l34 = { + code: "is", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Fyrri", + next: "Næsti", + today: "Í dag", + month: "Mánuður", + week: "Vika", + day: "Dagur", + list: "Dagskrá" + }, + weekText: "Vika", + allDayText: "Allan daginn", + moreLinkText: "meira", + noEventsText: "Engir viðburðir til að sýna" + }; + + var l35 = { + code: "it", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Prec", + next: "Succ", + today: "Oggi", + month: "Mese", + week: "Settimana", + day: "Giorno", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Tutto il giorno", + moreLinkText: function(n) { + return "+altri " + n; + }, + noEventsText: "Non ci sono eventi da visualizzare" + }; + + var l36 = { + code: "ja", + buttonText: { + prev: "前", + next: "次", + today: "今日", + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + weekText: "週", + allDayText: "終日", + moreLinkText: function(n) { + return "他 " + n + " 件"; + }, + noEventsText: "表示する予定はありません" + }; + + var l37 = { + code: "ka", + week: { + dow: 1, + doy: 7 + }, + buttonText: { + prev: "წინა", + next: "შემდეგი", + today: "დღეს", + month: "თვე", + week: "კვირა", + day: "დღე", + list: "დღის წესრიგი" + }, + weekText: "კვ", + allDayText: "მთელი დღე", + moreLinkText: function(n) { + return "+ კიდევ " + n; + }, + noEventsText: "ღონისძიებები არ არის" + }; + + var l38 = { + code: "kk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Алдыңғы", + next: "Келесі", + today: "Бүгін", + month: "Ай", + week: "Апта", + day: "Күн", + list: "Күн тәртібі" + }, + weekText: "Не", + allDayText: "Күні бойы", + moreLinkText: function(n) { + return "+ тағы " + n; + }, + noEventsText: "Көрсету үшін оқиғалар жоқ" + }; + + var l39 = { + code: "ko", + buttonText: { + prev: "이전달", + next: "다음달", + today: "오늘", + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + weekText: "주", + allDayText: "종일", + moreLinkText: "개", + noEventsText: "일정이 없습니다" + }; + + var l40 = { + code: "lb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zréck", + next: "Weider", + today: "Haut", + month: "Mount", + week: "Woch", + day: "Dag", + list: "Terminiwwersiicht" + }, + weekText: "W", + allDayText: "Ganzen Dag", + moreLinkText: "méi", + noEventsText: "Nee Evenementer ze affichéieren" + }; + + var l41 = { + code: "lt", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Atgal", + next: "Pirmyn", + today: "Šiandien", + month: "Mėnuo", + week: "Savaitė", + day: "Diena", + list: "Darbotvarkė" + }, + weekText: "SAV", + allDayText: "Visą dieną", + moreLinkText: "daugiau", + noEventsText: "Nėra įvykių rodyti" + }; + + var l42 = { + code: "lv", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Iepr.", + next: "Nāk.", + today: "Šodien", + month: "Mēnesis", + week: "Nedēļa", + day: "Diena", + list: "Dienas kārtība" + }, + weekText: "Ned.", + allDayText: "Visu dienu", + moreLinkText: function(n) { + return "+vēl " + n; + }, + noEventsText: "Nav notikumu" + }; + + var l43 = { + code: "mk", + buttonText: { + prev: "претходно", + next: "следно", + today: "Денес", + month: "Месец", + week: "Недела", + day: "Ден", + list: "График" + }, + weekText: "Сед", + allDayText: "Цел ден", + moreLinkText: function(n) { + return "+повеќе " + n; + }, + noEventsText: "Нема настани за прикажување" + }; + + var l44 = { + code: "ms", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Sebelum", + next: "Selepas", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekText: "Mg", + allDayText: "Sepanjang hari", + moreLinkText: function(n) { + return "masih ada " + n + " acara"; + }, + noEventsText: "Tiada peristiwa untuk dipaparkan" + }; + + var l45 = { + code: "nb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Neste", + today: "I dag", + month: "Måned", + week: "Uke", + day: "Dag", + list: "Agenda" + }, + weekText: "Uke", + allDayText: "Hele dagen", + moreLinkText: "til", + noEventsText: "Ingen hendelser å vise" + }; + + var l46 = { + code: "ne", //code for nepal + week: { + dow: 7, // Sunday is the first day of the week. + doy: 1 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "अघिल्लो", + next: "अर्को", + today: "आज", + month: "महिना", + week: "हप्ता", + day: "दिन", + list: "सूची" + }, + weekText: "हप्ता", + allDayText: "दिनभरि", + moreLinkText: "थप लिंक", + noEventsText: "देखाउनको लागि कुनै घटनाहरू छैनन्" + }; + + var l47 = { + code: "nl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Voorgaand", + next: "Volgende", + today: "Vandaag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dag", + moreLinkText: "extra", + noEventsText: "Geen evenementen om te laten zien" + }; + + var l48 = { + code: "nn", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Førre", + next: "Neste", + today: "I dag", + month: "Månad", + week: "Veke", + day: "Dag", + list: "Agenda" + }, + weekText: "Veke", + allDayText: "Heile dagen", + moreLinkText: "til", + noEventsText: "Ingen hendelser å vise" + }; + + var l49 = { + code: "pl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Poprzedni", + next: "Następny", + today: "Dziś", + month: "Miesiąc", + week: "Tydzień", + day: "Dzień", + list: "Plan dnia" + }, + weekText: "Tydz", + allDayText: "Cały dzień", + moreLinkText: "więcej", + noEventsText: "Brak wydarzeń do wyświetlenia" + }; + + var l50 = { + code: "pt-br", + buttonText: { + prev: "Anterior", + next: "Próximo", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Lista" + }, + weekText: "Sm", + allDayText: "dia inteiro", + moreLinkText: function(n) { + return "mais +" + n; + }, + noEventsText: "Não há eventos para mostrar" + }; + + var l51 = { + code: "pt", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Seguinte", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Agenda" + }, + weekText: "Sem", + allDayText: "Todo o dia", + moreLinkText: "mais", + noEventsText: "Não há eventos para mostrar" + }; + + var l52 = { + code: "ro", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "precedentă", + next: "următoare", + today: "Azi", + month: "Lună", + week: "Săptămână", + day: "Zi", + list: "Agendă" + }, + weekText: "Săpt", + allDayText: "Toată ziua", + moreLinkText: function(n) { + return "+alte " + n; + }, + noEventsText: "Nu există evenimente de afișat" + }; + + var l53 = { + code: "ru", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Пред", + next: "След", + today: "Сегодня", + month: "Месяц", + week: "Неделя", + day: "День", + list: "Повестка дня" + }, + weekText: "Нед", + allDayText: "Весь день", + moreLinkText: function(n) { + return "+ ещё " + n; + }, + noEventsText: "Нет событий для отображения" + }; + + var l54 = { + code: "sk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Predchádzajúci", + next: "Nasledujúci", + today: "Dnes", + month: "Mesiac", + week: "Týždeň", + day: "Deň", + list: "Rozvrh" + }, + weekText: "Ty", + allDayText: "Celý deň", + moreLinkText: function(n) { + return "+ďalšie: " + n; + }, + noEventsText: "Žiadne akcie na zobrazenie" + }; + + var l55 = { + code: "sl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prejšnji", + next: "Naslednji", + today: "Trenutni", + month: "Mesec", + week: "Teden", + day: "Dan", + list: "Dnevni red" + }, + weekText: "Teden", + allDayText: "Ves dan", + moreLinkText: "več", + noEventsText: "Ni dogodkov za prikaz" + }; + + var l56 = { + code: "sq", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "mbrapa", + next: "Përpara", + today: "sot", + month: "Muaj", + week: "Javë", + day: "Ditë", + list: "Listë" + }, + weekText: "Ja", + allDayText: "Gjithë ditën", + moreLinkText: function(n) { + return "+më tepër " + n; + }, + noEventsText: "Nuk ka evente për të shfaqur" + }; + + var l57 = { + code: "sr-cyrl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Претходна", + next: "следећи", + today: "Данас", + month: "Месец", + week: "Недеља", + day: "Дан", + list: "Планер" + }, + weekText: "Сед", + allDayText: "Цео дан", + moreLinkText: function(n) { + return "+ још " + n; + }, + noEventsText: "Нема догађаја за приказ" + }; + + var l58 = { + code: "sr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prethodna", + next: "Sledeći", + today: "Danas", + month: "Mеsеc", + week: "Nеdеlja", + day: "Dan", + list: "Planеr" + }, + weekText: "Sed", + allDayText: "Cеo dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nеma događaja za prikaz" + }; + + var l59 = { + code: "sv", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Förra", + next: "Nästa", + today: "Idag", + month: "Månad", + week: "Vecka", + day: "Dag", + list: "Program" + }, + weekText: "v.", + allDayText: "Heldag", + moreLinkText: "till", + noEventsText: "Inga händelser att visa" + }; + + var l60 = { + code: "th", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "ก่อนหน้า", + next: "ถัดไป", + prevYear: 'ปีก่อนหน้า', + nextYear: 'ปีถัดไป', + year: 'ปี', + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "กำหนดการ" + }, + weekText: "สัปดาห์", + allDayText: "ตลอดวัน", + moreLinkText: "เพิ่มเติม", + noEventsText: "ไม่มีกิจกรรมที่จะแสดง" + }; + + var l61 = { + code: "tr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "geri", + next: "ileri", + today: "bugün", + month: "Ay", + week: "Hafta", + day: "Gün", + list: "Ajanda" + }, + weekText: "Hf", + allDayText: "Tüm gün", + moreLinkText: "daha fazla", + noEventsText: "Gösterilecek etkinlik yok" + }; + + var l62 = { + code: "ug", + buttonText: { + month: "ئاي", + week: "ھەپتە", + day: "كۈن", + list: "كۈنتەرتىپ" + }, + allDayText: "پۈتۈن كۈن" + }; + + var l63 = { + code: "uk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Попередній", + next: "далі", + today: "Сьогодні", + month: "Місяць", + week: "Тиждень", + day: "День", + list: "Порядок денний" + }, + weekText: "Тиж", + allDayText: "Увесь день", + moreLinkText: function(n) { + return "+ще " + n + "..."; + }, + noEventsText: "Немає подій для відображення" + }; + + var l64 = { + code: "uz", + buttonText: { + month: "Oy", + week: "Xafta", + day: "Kun", + list: "Kun tartibi" + }, + allDayText: "Kun bo'yi", + moreLinkText: function(n) { + return "+ yana " + n; + }, + noEventsText: "Ko'rsatish uchun voqealar yo'q" + }; + + var l65 = { + code: "vi", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Trước", + next: "Tiếp", + today: "Hôm nay", + month: "Tháng", + week: "Tuần", + day: "Ngày", + list: "Lịch biểu" + }, + weekText: "Tu", + allDayText: "Cả ngày", + moreLinkText: function(n) { + return "+ thêm " + n; + }, + noEventsText: "Không có sự kiện để hiển thị" + }; + + var l66 = { + code: "zh-cn", + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "周", + day: "日", + list: "日程" + }, + weekText: "周", + allDayText: "全天", + moreLinkText: function(n) { + return "另外 " + n + " 个"; + }, + noEventsText: "没有事件显示" + }; + + var l67 = { + code: "zh-tw", + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "週", + day: "天", + list: "活動列表" + }, + weekText: "周", + allDayText: "整天", + moreLinkText: '顯示更多', + noEventsText: "没有任何活動" + }; + + var localesAll = [ + l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, + ]; + + return localesAll; + +}()); diff --git a/static/fullcalendar/locales-all.min.js b/static/fullcalendar/locales-all.min.js new file mode 100644 index 0000000..c55fe9b --- /dev/null +++ b/static/fullcalendar/locales-all.min.js @@ -0,0 +1 @@ +[].push.apply(FullCalendar.globalLocales,function(){"use strict";return[{code:"af",week:{dow:1,doy:4},buttonText:{prev:"Vorige",next:"Volgende",today:"Vandag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Heeldag",moreLinkText:"Addisionele",noEventsText:"Daar is geen gebeurtenisse nie"},{code:"ar-dz",week:{dow:0,doy:4},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar-kw",week:{dow:0,doy:12},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar-ly",week:{dow:6,doy:12},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar-ma",week:{dow:6,doy:12},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar-sa",week:{dow:0,doy:6},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar-tn",week:{dow:1,doy:4},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"ar",week:{dow:6,doy:12},direction:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekText:"أسبوع",allDayText:"اليوم كله",moreLinkText:"أخرى",noEventsText:"أي أحداث لعرض"},{code:"az",week:{dow:1,doy:4},buttonText:{prev:"Əvvəl",next:"Sonra",today:"Bu Gün",month:"Ay",week:"Həftə",day:"Gün",list:"Gündəm"},weekText:"Həftə",allDayText:"Bütün Gün",moreLinkText:function(e){return"+ daha çox "+e},noEventsText:"Göstərmək üçün hadisə yoxdur"},{code:"bg",week:{dow:1,doy:7},buttonText:{prev:"назад",next:"напред",today:"днес",month:"Месец",week:"Седмица",day:"Ден",list:"График"},allDayText:"Цял ден",moreLinkText:function(e){return"+още "+e},noEventsText:"Няма събития за показване"},{code:"bs",week:{dow:1,doy:7},buttonText:{prev:"Prošli",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Sedmica",day:"Dan",list:"Raspored"},weekText:"Sed",allDayText:"Cijeli dan",moreLinkText:function(e){return"+ još "+e},noEventsText:"Nema događaja za prikazivanje"},{code:"ca",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Següent",today:"Avui",month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},weekText:"Set",allDayText:"Tot el dia",moreLinkText:"més",noEventsText:"No hi ha esdeveniments per mostrar"},{code:"cs",week:{dow:1,doy:4},buttonText:{prev:"Dříve",next:"Později",today:"Nyní",month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},weekText:"Týd",allDayText:"Celý den",moreLinkText:function(e){return"+další: "+e},noEventsText:"Žádné akce k zobrazení"},{code:"da",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Næste",today:"I dag",month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},weekText:"Uge",allDayText:"Hele dagen",moreLinkText:"flere",noEventsText:"Ingen arrangementer at vise"},{code:"de",week:{dow:1,doy:4},buttonText:{prev:"Zurück",next:"Vor",today:"Heute",year:"Jahr",month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},weekText:"KW",allDayText:"Ganztägig",moreLinkText:function(e){return"+ weitere "+e},noEventsText:"Keine Ereignisse anzuzeigen"},{code:"el",week:{dow:1,doy:4},buttonText:{prev:"Προηγούμενος",next:"Επόμενος",today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},weekText:"Εβδ",allDayText:"Ολοήμερο",moreLinkText:"περισσότερα",noEventsText:"Δεν υπάρχουν γεγονότα προς εμφάνιση"},{code:"en-au",week:{dow:1,doy:4}},{code:"en-gb",week:{dow:1,doy:4}},{code:"en-nz",week:{dow:1,doy:4}},{code:"es",week:{dow:0,doy:6},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekText:"Sm",allDayText:"Todo el día",moreLinkText:"más",noEventsText:"No hay eventos para mostrar"},{code:"es",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekText:"Sm",allDayText:"Todo el día",moreLinkText:"más",noEventsText:"No hay eventos para mostrar"},{code:"et",week:{dow:1,doy:4},buttonText:{prev:"Eelnev",next:"Järgnev",today:"Täna",month:"Kuu",week:"Nädal",day:"Päev",list:"Päevakord"},weekText:"näd",allDayText:"Kogu päev",moreLinkText:function(e){return"+ veel "+e},noEventsText:"Kuvamiseks puuduvad sündmused"},{code:"eu",week:{dow:1,doy:7},buttonText:{prev:"Aur",next:"Hur",today:"Gaur",month:"Hilabetea",week:"Astea",day:"Eguna",list:"Agenda"},weekText:"As",allDayText:"Egun osoa",moreLinkText:"gehiago",noEventsText:"Ez dago ekitaldirik erakusteko"},{code:"fa",week:{dow:6,doy:12},direction:"rtl",buttonText:{prev:"قبلی",next:"بعدی",today:"امروز",month:"ماه",week:"هفته",day:"روز",list:"برنامه"},weekText:"هف",allDayText:"تمام روز",moreLinkText:function(e){return"بیش از "+e},noEventsText:"هیچ رویدادی به نمایش"},{code:"fi",week:{dow:1,doy:4},buttonText:{prev:"Edellinen",next:"Seuraava",today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},weekText:"Vk",allDayText:"Koko päivä",moreLinkText:"lisää",noEventsText:"Ei näytettäviä tapahtumia"},{code:"fr",buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekText:"Sem.",allDayText:"Toute la journée",moreLinkText:"en plus",noEventsText:"Aucun événement à afficher"},{code:"fr-ch",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Courant",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekText:"Sm",allDayText:"Toute la journée",moreLinkText:"en plus",noEventsText:"Aucun événement à afficher"},{code:"fr",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Planning"},weekText:"Sem.",allDayText:"Toute la journée",moreLinkText:"en plus",noEventsText:"Aucun événement à afficher"},{code:"gl",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Seg",today:"Hoxe",month:"Mes",week:"Semana",day:"Día",list:"Axenda"},weekText:"Sm",allDayText:"Todo o día",moreLinkText:"máis",noEventsText:"Non hai eventos para amosar"},{code:"he",direction:"rtl",buttonText:{prev:"הקודם",next:"הבא",today:"היום",month:"חודש",week:"שבוע",day:"יום",list:"סדר יום"},allDayText:"כל היום",moreLinkText:"אחר",noEventsText:"אין אירועים להצגה",weekText:"שבוע"},{code:"hi",week:{dow:0,doy:6},buttonText:{prev:"पिछला",next:"अगला",today:"आज",month:"महीना",week:"सप्ताह",day:"दिन",list:"कार्यसूची"},weekText:"हफ्ता",allDayText:"सभी दिन",moreLinkText:function(e){return"+अधिक "+e},noEventsText:"कोई घटनाओं को प्रदर्शित करने के लिए"},{code:"hr",week:{dow:1,doy:7},buttonText:{prev:"Prijašnji",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},weekText:"Tje",allDayText:"Cijeli dan",moreLinkText:function(e){return"+ još "+e},noEventsText:"Nema događaja za prikaz"},{code:"hu",week:{dow:1,doy:4},buttonText:{prev:"vissza",next:"előre",today:"ma",month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},weekText:"Hét",allDayText:"Egész nap",moreLinkText:"további",noEventsText:"Nincs megjeleníthető esemény"},{code:"id",week:{dow:1,doy:7},buttonText:{prev:"mundur",next:"maju",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekText:"Mg",allDayText:"Sehari penuh",moreLinkText:"lebih",noEventsText:"Tidak ada acara untuk ditampilkan"},{code:"is",week:{dow:1,doy:4},buttonText:{prev:"Fyrri",next:"Næsti",today:"Í dag",month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},weekText:"Vika",allDayText:"Allan daginn",moreLinkText:"meira",noEventsText:"Engir viðburðir til að sýna"},{code:"it",week:{dow:1,doy:4},buttonText:{prev:"Prec",next:"Succ",today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},weekText:"Sm",allDayText:"Tutto il giorno",moreLinkText:function(e){return"+altri "+e},noEventsText:"Non ci sono eventi da visualizzare"},{code:"ja",buttonText:{prev:"前",next:"次",today:"今日",month:"月",week:"週",day:"日",list:"予定リスト"},weekText:"週",allDayText:"終日",moreLinkText:function(e){return"他 "+e+" 件"},noEventsText:"表示する予定はありません"},{code:"ka",week:{dow:1,doy:7},buttonText:{prev:"წინა",next:"შემდეგი",today:"დღეს",month:"თვე",week:"კვირა",day:"დღე",list:"დღის წესრიგი"},weekText:"კვ",allDayText:"მთელი დღე",moreLinkText:function(e){return"+ კიდევ "+e},noEventsText:"ღონისძიებები არ არის"},{code:"kk",week:{dow:1,doy:7},buttonText:{prev:"Алдыңғы",next:"Келесі",today:"Бүгін",month:"Ай",week:"Апта",day:"Күн",list:"Күн тәртібі"},weekText:"Не",allDayText:"Күні бойы",moreLinkText:function(e){return"+ тағы "+e},noEventsText:"Көрсету үшін оқиғалар жоқ"},{code:"ko",buttonText:{prev:"이전달",next:"다음달",today:"오늘",month:"월",week:"주",day:"일",list:"일정목록"},weekText:"주",allDayText:"종일",moreLinkText:"개",noEventsText:"일정이 없습니다"},{code:"lb",week:{dow:1,doy:4},buttonText:{prev:"Zréck",next:"Weider",today:"Haut",month:"Mount",week:"Woch",day:"Dag",list:"Terminiwwersiicht"},weekText:"W",allDayText:"Ganzen Dag",moreLinkText:"méi",noEventsText:"Nee Evenementer ze affichéieren"},{code:"lt",week:{dow:1,doy:4},buttonText:{prev:"Atgal",next:"Pirmyn",today:"Šiandien",month:"Mėnuo",week:"Savaitė",day:"Diena",list:"Darbotvarkė"},weekText:"SAV",allDayText:"Visą dieną",moreLinkText:"daugiau",noEventsText:"Nėra įvykių rodyti"},{code:"lv",week:{dow:1,doy:4},buttonText:{prev:"Iepr.",next:"Nāk.",today:"Šodien",month:"Mēnesis",week:"Nedēļa",day:"Diena",list:"Dienas kārtība"},weekText:"Ned.",allDayText:"Visu dienu",moreLinkText:function(e){return"+vēl "+e},noEventsText:"Nav notikumu"},{code:"mk",buttonText:{prev:"претходно",next:"следно",today:"Денес",month:"Месец",week:"Недела",day:"Ден",list:"График"},weekText:"Сед",allDayText:"Цел ден",moreLinkText:function(e){return"+повеќе "+e},noEventsText:"Нема настани за прикажување"},{code:"ms",week:{dow:1,doy:7},buttonText:{prev:"Sebelum",next:"Selepas",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekText:"Mg",allDayText:"Sepanjang hari",moreLinkText:function(e){return"masih ada "+e+" acara"},noEventsText:"Tiada peristiwa untuk dipaparkan"},{code:"nb",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Neste",today:"I dag",month:"Måned",week:"Uke",day:"Dag",list:"Agenda"},weekText:"Uke",allDayText:"Hele dagen",moreLinkText:"til",noEventsText:"Ingen hendelser å vise"},{code:"ne",week:{dow:7,doy:1},buttonText:{prev:"अघिल्लो",next:"अर्को",today:"आज",month:"महिना",week:"हप्ता",day:"दिन",list:"सूची"},weekText:"हप्ता",allDayText:"दिनभरि",moreLinkText:"थप लिंक",noEventsText:"देखाउनको लागि कुनै घटनाहरू छैनन्"},{code:"nl",week:{dow:1,doy:4},buttonText:{prev:"Voorgaand",next:"Volgende",today:"Vandaag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",moreLinkText:"extra",noEventsText:"Geen evenementen om te laten zien"},{code:"nn",week:{dow:1,doy:4},buttonText:{prev:"Førre",next:"Neste",today:"I dag",month:"Månad",week:"Veke",day:"Dag",list:"Agenda"},weekText:"Veke",allDayText:"Heile dagen",moreLinkText:"til",noEventsText:"Ingen hendelser å vise"},{code:"pl",week:{dow:1,doy:4},buttonText:{prev:"Poprzedni",next:"Następny",today:"Dziś",month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},weekText:"Tydz",allDayText:"Cały dzień",moreLinkText:"więcej",noEventsText:"Brak wydarzeń do wyświetlenia"},{code:"pt-br",buttonText:{prev:"Anterior",next:"Próximo",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Lista"},weekText:"Sm",allDayText:"dia inteiro",moreLinkText:function(e){return"mais +"+e},noEventsText:"Não há eventos para mostrar"},{code:"pt",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Seguinte",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},weekText:"Sem",allDayText:"Todo o dia",moreLinkText:"mais",noEventsText:"Não há eventos para mostrar"},{code:"ro",week:{dow:1,doy:7},buttonText:{prev:"precedentă",next:"următoare",today:"Azi",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},weekText:"Săpt",allDayText:"Toată ziua",moreLinkText:function(e){return"+alte "+e},noEventsText:"Nu există evenimente de afișat"},{code:"ru",week:{dow:1,doy:4},buttonText:{prev:"Пред",next:"След",today:"Сегодня",month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},weekText:"Нед",allDayText:"Весь день",moreLinkText:function(e){return"+ ещё "+e},noEventsText:"Нет событий для отображения"},{code:"sk",week:{dow:1,doy:4},buttonText:{prev:"Predchádzajúci",next:"Nasledujúci",today:"Dnes",month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},weekText:"Ty",allDayText:"Celý deň",moreLinkText:function(e){return"+ďalšie: "+e},noEventsText:"Žiadne akcie na zobrazenie"},{code:"sl",week:{dow:1,doy:7},buttonText:{prev:"Prejšnji",next:"Naslednji",today:"Trenutni",month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},weekText:"Teden",allDayText:"Ves dan",moreLinkText:"več",noEventsText:"Ni dogodkov za prikaz"},{code:"sq",week:{dow:1,doy:4},buttonText:{prev:"mbrapa",next:"Përpara",today:"sot",month:"Muaj",week:"Javë",day:"Ditë",list:"Listë"},weekText:"Ja",allDayText:"Gjithë ditën",moreLinkText:function(e){return"+më tepër "+e},noEventsText:"Nuk ka evente për të shfaqur"},{code:"sr-cyrl",week:{dow:1,doy:7},buttonText:{prev:"Претходна",next:"следећи",today:"Данас",month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},weekText:"Сед",allDayText:"Цео дан",moreLinkText:function(e){return"+ још "+e},noEventsText:"Нема догађаја за приказ"},{code:"sr",week:{dow:1,doy:7},buttonText:{prev:"Prethodna",next:"Sledeći",today:"Danas",month:"Mеsеc",week:"Nеdеlja",day:"Dan",list:"Planеr"},weekText:"Sed",allDayText:"Cеo dan",moreLinkText:function(e){return"+ još "+e},noEventsText:"Nеma događaja za prikaz"},{code:"sv",week:{dow:1,doy:4},buttonText:{prev:"Förra",next:"Nästa",today:"Idag",month:"Månad",week:"Vecka",day:"Dag",list:"Program"},weekText:"v.",allDayText:"Heldag",moreLinkText:"till",noEventsText:"Inga händelser att visa"},{code:"th",week:{dow:1,doy:4},buttonText:{prev:"ก่อนหน้า",next:"ถัดไป",prevYear:"ปีก่อนหน้า",nextYear:"ปีถัดไป",year:"ปี",today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน",list:"กำหนดการ"},weekText:"สัปดาห์",allDayText:"ตลอดวัน",moreLinkText:"เพิ่มเติม",noEventsText:"ไม่มีกิจกรรมที่จะแสดง"},{code:"tr",week:{dow:1,doy:7},buttonText:{prev:"geri",next:"ileri",today:"bugün",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},weekText:"Hf",allDayText:"Tüm gün",moreLinkText:"daha fazla",noEventsText:"Gösterilecek etkinlik yok"},{code:"ug",buttonText:{month:"ئاي",week:"ھەپتە",day:"كۈن",list:"كۈنتەرتىپ"},allDayText:"پۈتۈن كۈن"},{code:"uk",week:{dow:1,doy:7},buttonText:{prev:"Попередній",next:"далі",today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День",list:"Порядок денний"},weekText:"Тиж",allDayText:"Увесь день",moreLinkText:function(e){return"+ще "+e+"..."},noEventsText:"Немає подій для відображення"},{code:"uz",buttonText:{month:"Oy",week:"Xafta",day:"Kun",list:"Kun tartibi"},allDayText:"Kun bo'yi",moreLinkText:function(e){return"+ yana "+e},noEventsText:"Ko'rsatish uchun voqealar yo'q"},{code:"vi",week:{dow:1,doy:4},buttonText:{prev:"Trước",next:"Tiếp",today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày",list:"Lịch biểu"},weekText:"Tu",allDayText:"Cả ngày",moreLinkText:function(e){return"+ thêm "+e},noEventsText:"Không có sự kiện để hiển thị"},{code:"zh-cn",week:{dow:1,doy:4},buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"周",day:"日",list:"日程"},weekText:"周",allDayText:"全天",moreLinkText:function(e){return"另外 "+e+" 个"},noEventsText:"没有事件显示"},{code:"zh-tw",buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"週",day:"天",list:"活動列表"},weekText:"周",allDayText:"整天",moreLinkText:"顯示更多",noEventsText:"没有任何活動"}]}()); \ No newline at end of file diff --git a/static/fullcalendar/locales/af.js b/static/fullcalendar/locales/af.js new file mode 100644 index 0000000..a9535f5 --- /dev/null +++ b/static/fullcalendar/locales/af.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var af = { + code: "af", + week: { + dow: 1, // Maandag is die eerste dag van die week. + doy: 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. + }, + buttonText: { + prev: "Vorige", + next: "Volgende", + today: "Vandag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Heeldag", + moreLinkText: "Addisionele", + noEventsText: "Daar is geen gebeurtenisse nie" + }; + + return af; + +}()); diff --git a/static/fullcalendar/locales/ar-dz.js b/static/fullcalendar/locales/ar-dz.js new file mode 100644 index 0000000..d351bcf --- /dev/null +++ b/static/fullcalendar/locales/ar-dz.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arDz = { + code: "ar-dz", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 4 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arDz; + +}()); diff --git a/static/fullcalendar/locales/ar-kw.js b/static/fullcalendar/locales/ar-kw.js new file mode 100644 index 0000000..080be1d --- /dev/null +++ b/static/fullcalendar/locales/ar-kw.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arKw = { + code: "ar-kw", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arKw; + +}()); diff --git a/static/fullcalendar/locales/ar-ly.js b/static/fullcalendar/locales/ar-ly.js new file mode 100644 index 0000000..9dfac0a --- /dev/null +++ b/static/fullcalendar/locales/ar-ly.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arLy = { + code: "ar-ly", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arLy; + +}()); diff --git a/static/fullcalendar/locales/ar-ma.js b/static/fullcalendar/locales/ar-ma.js new file mode 100644 index 0000000..a44ce7d --- /dev/null +++ b/static/fullcalendar/locales/ar-ma.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arMa = { + code: "ar-ma", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arMa; + +}()); diff --git a/static/fullcalendar/locales/ar-sa.js b/static/fullcalendar/locales/ar-sa.js new file mode 100644 index 0000000..554e3df --- /dev/null +++ b/static/fullcalendar/locales/ar-sa.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arSa = { + code: "ar-sa", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arSa; + +}()); diff --git a/static/fullcalendar/locales/ar-tn.js b/static/fullcalendar/locales/ar-tn.js new file mode 100644 index 0000000..d4d4750 --- /dev/null +++ b/static/fullcalendar/locales/ar-tn.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var arTn = { + code: "ar-tn", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return arTn; + +}()); diff --git a/static/fullcalendar/locales/ar.js b/static/fullcalendar/locales/ar.js new file mode 100644 index 0000000..462c353 --- /dev/null +++ b/static/fullcalendar/locales/ar.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ar = { + code: "ar", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekText: "أسبوع", + allDayText: "اليوم كله", + moreLinkText: "أخرى", + noEventsText: "أي أحداث لعرض" + }; + + return ar; + +}()); diff --git a/static/fullcalendar/locales/az.js b/static/fullcalendar/locales/az.js new file mode 100644 index 0000000..634b46b --- /dev/null +++ b/static/fullcalendar/locales/az.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var az = { + code: "az", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Əvvəl", + next: "Sonra", + today: "Bu Gün", + month: "Ay", + week: "Həftə", + day: "Gün", + list: "Gündəm" + }, + weekText: "Həftə", + allDayText: "Bütün Gün", + moreLinkText: function(n) { + return "+ daha çox " + n; + }, + noEventsText: "Göstərmək üçün hadisə yoxdur" + }; + + return az; + +}()); diff --git a/static/fullcalendar/locales/bg.js b/static/fullcalendar/locales/bg.js new file mode 100644 index 0000000..a50a26a --- /dev/null +++ b/static/fullcalendar/locales/bg.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var bg = { + code: "bg", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "назад", + next: "напред", + today: "днес", + month: "Месец", + week: "Седмица", + day: "Ден", + list: "График" + }, + allDayText: "Цял ден", + moreLinkText: function(n) { + return "+още " + n; + }, + noEventsText: "Няма събития за показване" + }; + + return bg; + +}()); diff --git a/static/fullcalendar/locales/bs.js b/static/fullcalendar/locales/bs.js new file mode 100644 index 0000000..80c9f48 --- /dev/null +++ b/static/fullcalendar/locales/bs.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var bs = { + code: "bs", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prošli", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Sedmica", + day: "Dan", + list: "Raspored" + }, + weekText: "Sed", + allDayText: "Cijeli dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nema događaja za prikazivanje" + }; + + return bs; + +}()); diff --git a/static/fullcalendar/locales/ca.js b/static/fullcalendar/locales/ca.js new file mode 100644 index 0000000..1e1d9b8 --- /dev/null +++ b/static/fullcalendar/locales/ca.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ca = { + code: "ca", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Següent", + today: "Avui", + month: "Mes", + week: "Setmana", + day: "Dia", + list: "Agenda" + }, + weekText: "Set", + allDayText: "Tot el dia", + moreLinkText: "més", + noEventsText: "No hi ha esdeveniments per mostrar" + }; + + return ca; + +}()); diff --git a/static/fullcalendar/locales/cs.js b/static/fullcalendar/locales/cs.js new file mode 100644 index 0000000..197b68c --- /dev/null +++ b/static/fullcalendar/locales/cs.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var cs = { + code: "cs", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Dříve", + next: "Později", + today: "Nyní", + month: "Měsíc", + week: "Týden", + day: "Den", + list: "Agenda" + }, + weekText: "Týd", + allDayText: "Celý den", + moreLinkText: function(n) { + return "+další: " + n; + }, + noEventsText: "Žádné akce k zobrazení" + }; + + return cs; + +}()); diff --git a/static/fullcalendar/locales/da.js b/static/fullcalendar/locales/da.js new file mode 100644 index 0000000..9db9d4a --- /dev/null +++ b/static/fullcalendar/locales/da.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var da = { + code: "da", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Næste", + today: "I dag", + month: "Måned", + week: "Uge", + day: "Dag", + list: "Agenda" + }, + weekText: "Uge", + allDayText: "Hele dagen", + moreLinkText: "flere", + noEventsText: "Ingen arrangementer at vise" + }; + + return da; + +}()); diff --git a/static/fullcalendar/locales/de.js b/static/fullcalendar/locales/de.js new file mode 100644 index 0000000..94374b9 --- /dev/null +++ b/static/fullcalendar/locales/de.js @@ -0,0 +1,30 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var de = { + code: "de", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zurück", + next: "Vor", + today: "Heute", + year: "Jahr", + month: "Monat", + week: "Woche", + day: "Tag", + list: "Terminübersicht" + }, + weekText: "KW", + allDayText: "Ganztägig", + moreLinkText: function(n) { + return "+ weitere " + n; + }, + noEventsText: "Keine Ereignisse anzuzeigen" + }; + + return de; + +}()); diff --git a/static/fullcalendar/locales/el.js b/static/fullcalendar/locales/el.js new file mode 100644 index 0000000..59c1a4c --- /dev/null +++ b/static/fullcalendar/locales/el.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var el = { + code: "el", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4st is the first week of the year. + }, + buttonText: { + prev: "Προηγούμενος", + next: "Επόμενος", + today: "Σήμερα", + month: "Μήνας", + week: "Εβδομάδα", + day: "Ημέρα", + list: "Ατζέντα" + }, + weekText: "Εβδ", + allDayText: "Ολοήμερο", + moreLinkText: "περισσότερα", + noEventsText: "Δεν υπάρχουν γεγονότα προς εμφάνιση" + }; + + return el; + +}()); diff --git a/static/fullcalendar/locales/en-au.js b/static/fullcalendar/locales/en-au.js new file mode 100644 index 0000000..9a21497 --- /dev/null +++ b/static/fullcalendar/locales/en-au.js @@ -0,0 +1,14 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var enAu = { + code: "en-au", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enAu; + +}()); diff --git a/static/fullcalendar/locales/en-gb.js b/static/fullcalendar/locales/en-gb.js new file mode 100644 index 0000000..e53ab94 --- /dev/null +++ b/static/fullcalendar/locales/en-gb.js @@ -0,0 +1,14 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var enGb = { + code: "en-gb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enGb; + +}()); diff --git a/static/fullcalendar/locales/en-nz.js b/static/fullcalendar/locales/en-nz.js new file mode 100644 index 0000000..27ffd67 --- /dev/null +++ b/static/fullcalendar/locales/en-nz.js @@ -0,0 +1,14 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var enNz = { + code: "en-nz", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enNz; + +}()); diff --git a/static/fullcalendar/locales/es-us.js b/static/fullcalendar/locales/es-us.js new file mode 100644 index 0000000..e91571d --- /dev/null +++ b/static/fullcalendar/locales/es-us.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var esUs = { + code: "es", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Todo el día", + moreLinkText: "más", + noEventsText: "No hay eventos para mostrar" + }; + + return esUs; + +}()); diff --git a/static/fullcalendar/locales/es.js b/static/fullcalendar/locales/es.js new file mode 100644 index 0000000..a7a61bb --- /dev/null +++ b/static/fullcalendar/locales/es.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var es = { + code: "es", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Todo el día", + moreLinkText: "más", + noEventsText: "No hay eventos para mostrar" + }; + + return es; + +}()); diff --git a/static/fullcalendar/locales/et.js b/static/fullcalendar/locales/et.js new file mode 100644 index 0000000..364e378 --- /dev/null +++ b/static/fullcalendar/locales/et.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var et = { + code: "et", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Eelnev", + next: "Järgnev", + today: "Täna", + month: "Kuu", + week: "Nädal", + day: "Päev", + list: "Päevakord" + }, + weekText: "näd", + allDayText: "Kogu päev", + moreLinkText: function(n) { + return "+ veel " + n; + }, + noEventsText: "Kuvamiseks puuduvad sündmused" + }; + + return et; + +}()); diff --git a/static/fullcalendar/locales/eu.js b/static/fullcalendar/locales/eu.js new file mode 100644 index 0000000..3554dc2 --- /dev/null +++ b/static/fullcalendar/locales/eu.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var eu = { + code: "eu", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Aur", + next: "Hur", + today: "Gaur", + month: "Hilabetea", + week: "Astea", + day: "Eguna", + list: "Agenda" + }, + weekText: "As", + allDayText: "Egun osoa", + moreLinkText: "gehiago", + noEventsText: "Ez dago ekitaldirik erakusteko" + }; + + return eu; + +}()); diff --git a/static/fullcalendar/locales/fa.js b/static/fullcalendar/locales/fa.js new file mode 100644 index 0000000..b0767b9 --- /dev/null +++ b/static/fullcalendar/locales/fa.js @@ -0,0 +1,30 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var fa = { + code: "fa", + week: { + dow: 6, // Saturday is the first day of the week. + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + direction: 'rtl', + buttonText: { + prev: "قبلی", + next: "بعدی", + today: "امروز", + month: "ماه", + week: "هفته", + day: "روز", + list: "برنامه" + }, + weekText: "هف", + allDayText: "تمام روز", + moreLinkText: function(n) { + return "بیش از " + n; + }, + noEventsText: "هیچ رویدادی به نمایش" + }; + + return fa; + +}()); diff --git a/static/fullcalendar/locales/fi.js b/static/fullcalendar/locales/fi.js new file mode 100644 index 0000000..3e8b575 --- /dev/null +++ b/static/fullcalendar/locales/fi.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var fi = { + code: "fi", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Edellinen", + next: "Seuraava", + today: "Tänään", + month: "Kuukausi", + week: "Viikko", + day: "Päivä", + list: "Tapahtumat" + }, + weekText: "Vk", + allDayText: "Koko päivä", + moreLinkText: "lisää", + noEventsText: "Ei näytettäviä tapahtumia" + }; + + return fi; + +}()); diff --git a/static/fullcalendar/locales/fr-ca.js b/static/fullcalendar/locales/fr-ca.js new file mode 100644 index 0000000..3f647e0 --- /dev/null +++ b/static/fullcalendar/locales/fr-ca.js @@ -0,0 +1,24 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var frCa = { + code: "fr", + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekText: "Sem.", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + return frCa; + +}()); diff --git a/static/fullcalendar/locales/fr-ch.js b/static/fullcalendar/locales/fr-ch.js new file mode 100644 index 0000000..d078771 --- /dev/null +++ b/static/fullcalendar/locales/fr-ch.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var frCh = { + code: "fr-ch", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Courant", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekText: "Sm", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + return frCh; + +}()); diff --git a/static/fullcalendar/locales/fr.js b/static/fullcalendar/locales/fr.js new file mode 100644 index 0000000..6948d41 --- /dev/null +++ b/static/fullcalendar/locales/fr.js @@ -0,0 +1,28 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var fr = { + code: "fr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Planning" + }, + weekText: "Sem.", + allDayText: "Toute la journée", + moreLinkText: "en plus", + noEventsText: "Aucun événement à afficher" + }; + + return fr; + +}()); diff --git a/static/fullcalendar/locales/gl.js b/static/fullcalendar/locales/gl.js new file mode 100644 index 0000000..bb090b9 --- /dev/null +++ b/static/fullcalendar/locales/gl.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var gl = { + code: "gl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Seg", + today: "Hoxe", + month: "Mes", + week: "Semana", + day: "Día", + list: "Axenda" + }, + weekText: "Sm", + allDayText: "Todo o día", + moreLinkText: "máis", + noEventsText: "Non hai eventos para amosar" + }; + + return gl; + +}()); diff --git a/static/fullcalendar/locales/he.js b/static/fullcalendar/locales/he.js new file mode 100644 index 0000000..e1848d8 --- /dev/null +++ b/static/fullcalendar/locales/he.js @@ -0,0 +1,24 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var he = { + code: "he", + direction: 'rtl', + buttonText: { + prev: "הקודם", + next: "הבא", + today: "היום", + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + moreLinkText: "אחר", + noEventsText: "אין אירועים להצגה", + weekText: "שבוע" + }; + + return he; + +}()); diff --git a/static/fullcalendar/locales/hi.js b/static/fullcalendar/locales/hi.js new file mode 100644 index 0000000..2e52eee --- /dev/null +++ b/static/fullcalendar/locales/hi.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var hi = { + code: "hi", + week: { + dow: 0, // Sunday is the first day of the week. + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "पिछला", + next: "अगला", + today: "आज", + month: "महीना", + week: "सप्ताह", + day: "दिन", + list: "कार्यसूची" + }, + weekText: "हफ्ता", + allDayText: "सभी दिन", + moreLinkText: function(n) { + return "+अधिक " + n; + }, + noEventsText: "कोई घटनाओं को प्रदर्शित करने के लिए" + }; + + return hi; + +}()); diff --git a/static/fullcalendar/locales/hr.js b/static/fullcalendar/locales/hr.js new file mode 100644 index 0000000..23d5550 --- /dev/null +++ b/static/fullcalendar/locales/hr.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var hr = { + code: "hr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prijašnji", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Tjedan", + day: "Dan", + list: "Raspored" + }, + weekText: "Tje", + allDayText: "Cijeli dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nema događaja za prikaz" + }; + + return hr; + +}()); diff --git a/static/fullcalendar/locales/hu.js b/static/fullcalendar/locales/hu.js new file mode 100644 index 0000000..ff4dd55 --- /dev/null +++ b/static/fullcalendar/locales/hu.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var hu = { + code: "hu", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "vissza", + next: "előre", + today: "ma", + month: "Hónap", + week: "Hét", + day: "Nap", + list: "Napló" + }, + weekText: "Hét", + allDayText: "Egész nap", + moreLinkText: "további", + noEventsText: "Nincs megjeleníthető esemény" + }; + + return hu; + +}()); diff --git a/static/fullcalendar/locales/id.js b/static/fullcalendar/locales/id.js new file mode 100644 index 0000000..4e43a3e --- /dev/null +++ b/static/fullcalendar/locales/id.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var id = { + code: "id", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "mundur", + next: "maju", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekText: "Mg", + allDayText: "Sehari penuh", + moreLinkText: "lebih", + noEventsText: "Tidak ada acara untuk ditampilkan" + }; + + return id; + +}()); diff --git a/static/fullcalendar/locales/is.js b/static/fullcalendar/locales/is.js new file mode 100644 index 0000000..df99e26 --- /dev/null +++ b/static/fullcalendar/locales/is.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var is = { + code: "is", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Fyrri", + next: "Næsti", + today: "Í dag", + month: "Mánuður", + week: "Vika", + day: "Dagur", + list: "Dagskrá" + }, + weekText: "Vika", + allDayText: "Allan daginn", + moreLinkText: "meira", + noEventsText: "Engir viðburðir til að sýna" + }; + + return is; + +}()); diff --git a/static/fullcalendar/locales/it.js b/static/fullcalendar/locales/it.js new file mode 100644 index 0000000..772f459 --- /dev/null +++ b/static/fullcalendar/locales/it.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var it = { + code: "it", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Prec", + next: "Succ", + today: "Oggi", + month: "Mese", + week: "Settimana", + day: "Giorno", + list: "Agenda" + }, + weekText: "Sm", + allDayText: "Tutto il giorno", + moreLinkText: function(n) { + return "+altri " + n; + }, + noEventsText: "Non ci sono eventi da visualizzare" + }; + + return it; + +}()); diff --git a/static/fullcalendar/locales/ja.js b/static/fullcalendar/locales/ja.js new file mode 100644 index 0000000..79fad27 --- /dev/null +++ b/static/fullcalendar/locales/ja.js @@ -0,0 +1,25 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ja = { + code: "ja", + buttonText: { + prev: "前", + next: "次", + today: "今日", + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + weekText: "週", + allDayText: "終日", + moreLinkText: function(n) { + return "他 " + n + " 件"; + }, + noEventsText: "表示する予定はありません" + }; + + return ja; + +}()); diff --git a/static/fullcalendar/locales/ka.js b/static/fullcalendar/locales/ka.js new file mode 100644 index 0000000..20de180 --- /dev/null +++ b/static/fullcalendar/locales/ka.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ka = { + code: "ka", + week: { + dow: 1, + doy: 7 + }, + buttonText: { + prev: "წინა", + next: "შემდეგი", + today: "დღეს", + month: "თვე", + week: "კვირა", + day: "დღე", + list: "დღის წესრიგი" + }, + weekText: "კვ", + allDayText: "მთელი დღე", + moreLinkText: function(n) { + return "+ კიდევ " + n; + }, + noEventsText: "ღონისძიებები არ არის" + }; + + return ka; + +}()); diff --git a/static/fullcalendar/locales/kk.js b/static/fullcalendar/locales/kk.js new file mode 100644 index 0000000..1bbc2ef --- /dev/null +++ b/static/fullcalendar/locales/kk.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var kk = { + code: "kk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Алдыңғы", + next: "Келесі", + today: "Бүгін", + month: "Ай", + week: "Апта", + day: "Күн", + list: "Күн тәртібі" + }, + weekText: "Не", + allDayText: "Күні бойы", + moreLinkText: function(n) { + return "+ тағы " + n; + }, + noEventsText: "Көрсету үшін оқиғалар жоқ" + }; + + return kk; + +}()); diff --git a/static/fullcalendar/locales/ko.js b/static/fullcalendar/locales/ko.js new file mode 100644 index 0000000..65f7ea1 --- /dev/null +++ b/static/fullcalendar/locales/ko.js @@ -0,0 +1,23 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ko = { + code: "ko", + buttonText: { + prev: "이전달", + next: "다음달", + today: "오늘", + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + weekText: "주", + allDayText: "종일", + moreLinkText: "개", + noEventsText: "일정이 없습니다" + }; + + return ko; + +}()); diff --git a/static/fullcalendar/locales/lb.js b/static/fullcalendar/locales/lb.js new file mode 100644 index 0000000..2faf1de --- /dev/null +++ b/static/fullcalendar/locales/lb.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var lb = { + code: "lb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zréck", + next: "Weider", + today: "Haut", + month: "Mount", + week: "Woch", + day: "Dag", + list: "Terminiwwersiicht" + }, + weekText: "W", + allDayText: "Ganzen Dag", + moreLinkText: "méi", + noEventsText: "Nee Evenementer ze affichéieren" + }; + + return lb; + +}()); diff --git a/static/fullcalendar/locales/lt.js b/static/fullcalendar/locales/lt.js new file mode 100644 index 0000000..50c4611 --- /dev/null +++ b/static/fullcalendar/locales/lt.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var lt = { + code: "lt", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Atgal", + next: "Pirmyn", + today: "Šiandien", + month: "Mėnuo", + week: "Savaitė", + day: "Diena", + list: "Darbotvarkė" + }, + weekText: "SAV", + allDayText: "Visą dieną", + moreLinkText: "daugiau", + noEventsText: "Nėra įvykių rodyti" + }; + + return lt; + +}()); diff --git a/static/fullcalendar/locales/lv.js b/static/fullcalendar/locales/lv.js new file mode 100644 index 0000000..9598b26 --- /dev/null +++ b/static/fullcalendar/locales/lv.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var lv = { + code: "lv", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Iepr.", + next: "Nāk.", + today: "Šodien", + month: "Mēnesis", + week: "Nedēļa", + day: "Diena", + list: "Dienas kārtība" + }, + weekText: "Ned.", + allDayText: "Visu dienu", + moreLinkText: function(n) { + return "+vēl " + n; + }, + noEventsText: "Nav notikumu" + }; + + return lv; + +}()); diff --git a/static/fullcalendar/locales/mk.js b/static/fullcalendar/locales/mk.js new file mode 100644 index 0000000..d903473 --- /dev/null +++ b/static/fullcalendar/locales/mk.js @@ -0,0 +1,25 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var mk = { + code: "mk", + buttonText: { + prev: "претходно", + next: "следно", + today: "Денес", + month: "Месец", + week: "Недела", + day: "Ден", + list: "График" + }, + weekText: "Сед", + allDayText: "Цел ден", + moreLinkText: function(n) { + return "+повеќе " + n; + }, + noEventsText: "Нема настани за прикажување" + }; + + return mk; + +}()); diff --git a/static/fullcalendar/locales/ms.js b/static/fullcalendar/locales/ms.js new file mode 100644 index 0000000..3d4fdb3 --- /dev/null +++ b/static/fullcalendar/locales/ms.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ms = { + code: "ms", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Sebelum", + next: "Selepas", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekText: "Mg", + allDayText: "Sepanjang hari", + moreLinkText: function(n) { + return "masih ada " + n + " acara"; + }, + noEventsText: "Tiada peristiwa untuk dipaparkan" + }; + + return ms; + +}()); diff --git a/static/fullcalendar/locales/nb.js b/static/fullcalendar/locales/nb.js new file mode 100644 index 0000000..9512e07 --- /dev/null +++ b/static/fullcalendar/locales/nb.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var nb = { + code: "nb", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Neste", + today: "I dag", + month: "Måned", + week: "Uke", + day: "Dag", + list: "Agenda" + }, + weekText: "Uke", + allDayText: "Hele dagen", + moreLinkText: "til", + noEventsText: "Ingen hendelser å vise" + }; + + return nb; + +}()); diff --git a/static/fullcalendar/locales/ne.js b/static/fullcalendar/locales/ne.js new file mode 100644 index 0000000..6b2c608 --- /dev/null +++ b/static/fullcalendar/locales/ne.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ne = { + code: "ne", //code for nepal + week: { + dow: 7, // Sunday is the first day of the week. + doy: 1 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "अघिल्लो", + next: "अर्को", + today: "आज", + month: "महिना", + week: "हप्ता", + day: "दिन", + list: "सूची" + }, + weekText: "हप्ता", + allDayText: "दिनभरि", + moreLinkText: "थप लिंक", + noEventsText: "देखाउनको लागि कुनै घटनाहरू छैनन्" + }; + + return ne; + +}()); diff --git a/static/fullcalendar/locales/nl.js b/static/fullcalendar/locales/nl.js new file mode 100644 index 0000000..a485abf --- /dev/null +++ b/static/fullcalendar/locales/nl.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var nl = { + code: "nl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Voorgaand", + next: "Volgende", + today: "Vandaag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dag", + moreLinkText: "extra", + noEventsText: "Geen evenementen om te laten zien" + }; + + return nl; + +}()); diff --git a/static/fullcalendar/locales/nn.js b/static/fullcalendar/locales/nn.js new file mode 100644 index 0000000..692127e --- /dev/null +++ b/static/fullcalendar/locales/nn.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var nn = { + code: "nn", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Førre", + next: "Neste", + today: "I dag", + month: "Månad", + week: "Veke", + day: "Dag", + list: "Agenda" + }, + weekText: "Veke", + allDayText: "Heile dagen", + moreLinkText: "til", + noEventsText: "Ingen hendelser å vise" + }; + + return nn; + +}()); diff --git a/static/fullcalendar/locales/pl.js b/static/fullcalendar/locales/pl.js new file mode 100644 index 0000000..ed21adf --- /dev/null +++ b/static/fullcalendar/locales/pl.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var pl = { + code: "pl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Poprzedni", + next: "Następny", + today: "Dziś", + month: "Miesiąc", + week: "Tydzień", + day: "Dzień", + list: "Plan dnia" + }, + weekText: "Tydz", + allDayText: "Cały dzień", + moreLinkText: "więcej", + noEventsText: "Brak wydarzeń do wyświetlenia" + }; + + return pl; + +}()); diff --git a/static/fullcalendar/locales/pt-br.js b/static/fullcalendar/locales/pt-br.js new file mode 100644 index 0000000..92744d9 --- /dev/null +++ b/static/fullcalendar/locales/pt-br.js @@ -0,0 +1,25 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ptBr = { + code: "pt-br", + buttonText: { + prev: "Anterior", + next: "Próximo", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Lista" + }, + weekText: "Sm", + allDayText: "dia inteiro", + moreLinkText: function(n) { + return "mais +" + n; + }, + noEventsText: "Não há eventos para mostrar" + }; + + return ptBr; + +}()); diff --git a/static/fullcalendar/locales/pt.js b/static/fullcalendar/locales/pt.js new file mode 100644 index 0000000..b72a79b --- /dev/null +++ b/static/fullcalendar/locales/pt.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var pt = { + code: "pt", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Seguinte", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Agenda" + }, + weekText: "Sem", + allDayText: "Todo o dia", + moreLinkText: "mais", + noEventsText: "Não há eventos para mostrar" + }; + + return pt; + +}()); diff --git a/static/fullcalendar/locales/ro.js b/static/fullcalendar/locales/ro.js new file mode 100644 index 0000000..0f833f3 --- /dev/null +++ b/static/fullcalendar/locales/ro.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ro = { + code: "ro", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "precedentă", + next: "următoare", + today: "Azi", + month: "Lună", + week: "Săptămână", + day: "Zi", + list: "Agendă" + }, + weekText: "Săpt", + allDayText: "Toată ziua", + moreLinkText: function(n) { + return "+alte " + n; + }, + noEventsText: "Nu există evenimente de afișat" + }; + + return ro; + +}()); diff --git a/static/fullcalendar/locales/ru.js b/static/fullcalendar/locales/ru.js new file mode 100644 index 0000000..0cf61d6 --- /dev/null +++ b/static/fullcalendar/locales/ru.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ru = { + code: "ru", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Пред", + next: "След", + today: "Сегодня", + month: "Месяц", + week: "Неделя", + day: "День", + list: "Повестка дня" + }, + weekText: "Нед", + allDayText: "Весь день", + moreLinkText: function(n) { + return "+ ещё " + n; + }, + noEventsText: "Нет событий для отображения" + }; + + return ru; + +}()); diff --git a/static/fullcalendar/locales/sk.js b/static/fullcalendar/locales/sk.js new file mode 100644 index 0000000..a46f85d --- /dev/null +++ b/static/fullcalendar/locales/sk.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var sk = { + code: "sk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Predchádzajúci", + next: "Nasledujúci", + today: "Dnes", + month: "Mesiac", + week: "Týždeň", + day: "Deň", + list: "Rozvrh" + }, + weekText: "Ty", + allDayText: "Celý deň", + moreLinkText: function(n) { + return "+ďalšie: " + n; + }, + noEventsText: "Žiadne akcie na zobrazenie" + }; + + return sk; + +}()); diff --git a/static/fullcalendar/locales/sl.js b/static/fullcalendar/locales/sl.js new file mode 100644 index 0000000..56cc360 --- /dev/null +++ b/static/fullcalendar/locales/sl.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var sl = { + code: "sl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prejšnji", + next: "Naslednji", + today: "Trenutni", + month: "Mesec", + week: "Teden", + day: "Dan", + list: "Dnevni red" + }, + weekText: "Teden", + allDayText: "Ves dan", + moreLinkText: "več", + noEventsText: "Ni dogodkov za prikaz" + }; + + return sl; + +}()); diff --git a/static/fullcalendar/locales/sq.js b/static/fullcalendar/locales/sq.js new file mode 100644 index 0000000..8e2ac0b --- /dev/null +++ b/static/fullcalendar/locales/sq.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var sq = { + code: "sq", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "mbrapa", + next: "Përpara", + today: "sot", + month: "Muaj", + week: "Javë", + day: "Ditë", + list: "Listë" + }, + weekText: "Ja", + allDayText: "Gjithë ditën", + moreLinkText: function(n) { + return "+më tepër " + n; + }, + noEventsText: "Nuk ka evente për të shfaqur" + }; + + return sq; + +}()); diff --git a/static/fullcalendar/locales/sr-cyrl.js b/static/fullcalendar/locales/sr-cyrl.js new file mode 100644 index 0000000..c650d70 --- /dev/null +++ b/static/fullcalendar/locales/sr-cyrl.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var srCyrl = { + code: "sr-cyrl", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Претходна", + next: "следећи", + today: "Данас", + month: "Месец", + week: "Недеља", + day: "Дан", + list: "Планер" + }, + weekText: "Сед", + allDayText: "Цео дан", + moreLinkText: function(n) { + return "+ још " + n; + }, + noEventsText: "Нема догађаја за приказ" + }; + + return srCyrl; + +}()); diff --git a/static/fullcalendar/locales/sr.js b/static/fullcalendar/locales/sr.js new file mode 100644 index 0000000..74a949d --- /dev/null +++ b/static/fullcalendar/locales/sr.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var sr = { + code: "sr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prethodna", + next: "Sledeći", + today: "Danas", + month: "Mеsеc", + week: "Nеdеlja", + day: "Dan", + list: "Planеr" + }, + weekText: "Sed", + allDayText: "Cеo dan", + moreLinkText: function(n) { + return "+ još " + n; + }, + noEventsText: "Nеma događaja za prikaz" + }; + + return sr; + +}()); diff --git a/static/fullcalendar/locales/sv.js b/static/fullcalendar/locales/sv.js new file mode 100644 index 0000000..b3a809d --- /dev/null +++ b/static/fullcalendar/locales/sv.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var sv = { + code: "sv", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Förra", + next: "Nästa", + today: "Idag", + month: "Månad", + week: "Vecka", + day: "Dag", + list: "Program" + }, + weekText: "v.", + allDayText: "Heldag", + moreLinkText: "till", + noEventsText: "Inga händelser att visa" + }; + + return sv; + +}()); diff --git a/static/fullcalendar/locales/th.js b/static/fullcalendar/locales/th.js new file mode 100644 index 0000000..927b392 --- /dev/null +++ b/static/fullcalendar/locales/th.js @@ -0,0 +1,30 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var th = { + code: "th", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "ก่อนหน้า", + next: "ถัดไป", + prevYear: 'ปีก่อนหน้า', + nextYear: 'ปีถัดไป', + year: 'ปี', + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "กำหนดการ" + }, + weekText: "สัปดาห์", + allDayText: "ตลอดวัน", + moreLinkText: "เพิ่มเติม", + noEventsText: "ไม่มีกิจกรรมที่จะแสดง" + }; + + return th; + +}()); diff --git a/static/fullcalendar/locales/tr.js b/static/fullcalendar/locales/tr.js new file mode 100644 index 0000000..dd19a44 --- /dev/null +++ b/static/fullcalendar/locales/tr.js @@ -0,0 +1,27 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var tr = { + code: "tr", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "geri", + next: "ileri", + today: "bugün", + month: "Ay", + week: "Hafta", + day: "Gün", + list: "Ajanda" + }, + weekText: "Hf", + allDayText: "Tüm gün", + moreLinkText: "daha fazla", + noEventsText: "Gösterilecek etkinlik yok" + }; + + return tr; + +}()); diff --git a/static/fullcalendar/locales/ug.js b/static/fullcalendar/locales/ug.js new file mode 100644 index 0000000..9130a6c --- /dev/null +++ b/static/fullcalendar/locales/ug.js @@ -0,0 +1,17 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var ug = { + code: "ug", + buttonText: { + month: "ئاي", + week: "ھەپتە", + day: "كۈن", + list: "كۈنتەرتىپ" + }, + allDayText: "پۈتۈن كۈن" + }; + + return ug; + +}()); diff --git a/static/fullcalendar/locales/uk.js b/static/fullcalendar/locales/uk.js new file mode 100644 index 0000000..acde239 --- /dev/null +++ b/static/fullcalendar/locales/uk.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var uk = { + code: "uk", + week: { + dow: 1, // Monday is the first day of the week. + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Попередній", + next: "далі", + today: "Сьогодні", + month: "Місяць", + week: "Тиждень", + day: "День", + list: "Порядок денний" + }, + weekText: "Тиж", + allDayText: "Увесь день", + moreLinkText: function(n) { + return "+ще " + n + "..."; + }, + noEventsText: "Немає подій для відображення" + }; + + return uk; + +}()); diff --git a/static/fullcalendar/locales/uz.js b/static/fullcalendar/locales/uz.js new file mode 100644 index 0000000..f1ea640 --- /dev/null +++ b/static/fullcalendar/locales/uz.js @@ -0,0 +1,21 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var uz = { + code: "uz", + buttonText: { + month: "Oy", + week: "Xafta", + day: "Kun", + list: "Kun tartibi" + }, + allDayText: "Kun bo'yi", + moreLinkText: function(n) { + return "+ yana " + n; + }, + noEventsText: "Ko'rsatish uchun voqealar yo'q" + }; + + return uz; + +}()); diff --git a/static/fullcalendar/locales/vi.js b/static/fullcalendar/locales/vi.js new file mode 100644 index 0000000..5ea3a4a --- /dev/null +++ b/static/fullcalendar/locales/vi.js @@ -0,0 +1,29 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var vi = { + code: "vi", + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Trước", + next: "Tiếp", + today: "Hôm nay", + month: "Tháng", + week: "Tuần", + day: "Ngày", + list: "Lịch biểu" + }, + weekText: "Tu", + allDayText: "Cả ngày", + moreLinkText: function(n) { + return "+ thêm " + n; + }, + noEventsText: "Không có sự kiện để hiển thị" + }; + + return vi; + +}()); diff --git a/static/fullcalendar/locales/zh-cn.js b/static/fullcalendar/locales/zh-cn.js new file mode 100644 index 0000000..45e6863 --- /dev/null +++ b/static/fullcalendar/locales/zh-cn.js @@ -0,0 +1,30 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var zhCn = { + code: "zh-cn", + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "周", + day: "日", + list: "日程" + }, + weekText: "周", + allDayText: "全天", + moreLinkText: function(n) { + return "另外 " + n + " 个"; + }, + noEventsText: "没有事件显示" + }; + + return zhCn; + +}()); diff --git a/static/fullcalendar/locales/zh-tw.js b/static/fullcalendar/locales/zh-tw.js new file mode 100644 index 0000000..0e9ccca --- /dev/null +++ b/static/fullcalendar/locales/zh-tw.js @@ -0,0 +1,23 @@ +FullCalendar.globalLocales.push(function () { + 'use strict'; + + var zhTw = { + code: "zh-tw", + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "週", + day: "天", + list: "活動列表" + }, + weekText: "周", + allDayText: "整天", + moreLinkText: '顯示更多', + noEventsText: "没有任何活動" + }; + + return zhTw; + +}()); diff --git a/static/fullcalendar/main.css b/static/fullcalendar/main.css new file mode 100644 index 0000000..2b276f5 --- /dev/null +++ b/static/fullcalendar/main.css @@ -0,0 +1,1429 @@ + +/* classes attached to */ + +.fc-not-allowed, +.fc-not-allowed .fc-event { /* override events' custom cursors */ + cursor: not-allowed; +} + +.fc-unselectable { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +.fc { + /* layout of immediate children */ + display: flex; + flex-direction: column; + + font-size: 1em +} +.fc, + .fc *, + .fc *:before, + .fc *:after { + box-sizing: border-box; + } +.fc table { + border-collapse: collapse; + border-spacing: 0; + font-size: 1em; /* normalize cross-browser */ + } +.fc th { + text-align: center; + } +.fc th, + .fc td { + vertical-align: top; + padding: 0; + } +.fc a[data-navlink] { + cursor: pointer; + } +.fc a[data-navlink]:hover { + text-decoration: underline; + } +.fc-direction-ltr { + direction: ltr; + text-align: left; +} +.fc-direction-rtl { + direction: rtl; + text-align: right; +} +.fc-theme-standard td, + .fc-theme-standard th { + border: 1px solid #ddd; + border: 1px solid var(--fc-border-color, #ddd); + } +/* for FF, which doesn't expand a 100% div within a table cell. use absolute positioning */ +/* inner-wrappers are responsible for being absolute */ +/* TODO: best place for this? */ +.fc-liquid-hack td, + .fc-liquid-hack th { + position: relative; + } + +@font-face { + font-family: 'fcicons'; + src: url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format('truetype'); + font-weight: normal; + font-style: normal; +} + +.fc-icon { + /* added for fc */ + display: inline-block; + width: 1em; + height: 1em; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'fcicons' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.fc-icon-chevron-left:before { + content: "\e900"; +} + +.fc-icon-chevron-right:before { + content: "\e901"; +} + +.fc-icon-chevrons-left:before { + content: "\e902"; +} + +.fc-icon-chevrons-right:before { + content: "\e903"; +} + +.fc-icon-minus-square:before { + content: "\e904"; +} + +.fc-icon-plus-square:before { + content: "\e905"; +} + +.fc-icon-x:before { + content: "\e906"; +} +/* +Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css + +These styles only apply when the standard-theme is activated. +When it's NOT activated, the fc-button classes won't even be in the DOM. +*/ +.fc { + + /* reset */ + +} +.fc .fc-button { + border-radius: 0; + overflow: visible; + text-transform: none; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; + } +.fc .fc-button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; + } +.fc .fc-button { + -webkit-appearance: button; + } +.fc .fc-button:not(:disabled) { + cursor: pointer; + } +.fc .fc-button::-moz-focus-inner { + padding: 0; + border-style: none; + } +.fc { + + /* theme */ + +} +.fc .fc-button { + display: inline-block; + font-weight: 400; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.4em 0.65em; + font-size: 1em; + line-height: 1.5; + border-radius: 0.25em; + } +.fc .fc-button:hover { + text-decoration: none; + } +.fc .fc-button:focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25); + } +.fc .fc-button:disabled { + opacity: 0.65; + } +.fc { + + /* "primary" coloring */ + +} +.fc .fc-button-primary { + color: #fff; + color: var(--fc-button-text-color, #fff); + background-color: #2C3E50; + background-color: var(--fc-button-bg-color, #2C3E50); + border-color: #2C3E50; + border-color: var(--fc-button-border-color, #2C3E50); + } +.fc .fc-button-primary:hover { + color: #fff; + color: var(--fc-button-text-color, #fff); + background-color: #1e2b37; + background-color: var(--fc-button-hover-bg-color, #1e2b37); + border-color: #1a252f; + border-color: var(--fc-button-hover-border-color, #1a252f); + } +.fc .fc-button-primary:disabled { /* not DRY */ + color: #fff; + color: var(--fc-button-text-color, #fff); + background-color: #2C3E50; + background-color: var(--fc-button-bg-color, #2C3E50); + border-color: #2C3E50; + border-color: var(--fc-button-border-color, #2C3E50); /* overrides :hover */ + } +.fc .fc-button-primary:focus { + box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); + } +.fc .fc-button-primary:not(:disabled):active, + .fc .fc-button-primary:not(:disabled).fc-button-active { + color: #fff; + color: var(--fc-button-text-color, #fff); + background-color: #1a252f; + background-color: var(--fc-button-active-bg-color, #1a252f); + border-color: #151e27; + border-color: var(--fc-button-active-border-color, #151e27); + } +.fc .fc-button-primary:not(:disabled):active:focus, + .fc .fc-button-primary:not(:disabled).fc-button-active:focus { + box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); + } +.fc { + + /* icons within buttons */ + +} +.fc .fc-button .fc-icon { + vertical-align: middle; + font-size: 1.5em; /* bump up the size (but don't make it bigger than line-height of button, which is 1.5em also) */ + } +.fc .fc-button-group { + position: relative; + display: inline-flex; + vertical-align: middle; + } +.fc .fc-button-group > .fc-button { + position: relative; + flex: 1 1 auto; + } +.fc .fc-button-group > .fc-button:hover { + z-index: 1; + } +.fc .fc-button-group > .fc-button:focus, + .fc .fc-button-group > .fc-button:active, + .fc .fc-button-group > .fc-button.fc-button-active { + z-index: 1; + } +.fc-direction-ltr .fc-button-group > .fc-button:not(:first-child) { + margin-left: -1px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +.fc-direction-ltr .fc-button-group > .fc-button:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +.fc-direction-rtl .fc-button-group > .fc-button:not(:first-child) { + margin-right: -1px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +.fc-direction-rtl .fc-button-group > .fc-button:not(:last-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +.fc .fc-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + } +.fc .fc-toolbar.fc-header-toolbar { + margin-bottom: 1.5em; + } +.fc .fc-toolbar.fc-footer-toolbar { + margin-top: 1.5em; + } +.fc .fc-toolbar-title { + font-size: 1.75em; + margin: 0; + } +.fc-direction-ltr .fc-toolbar > * > :not(:first-child) { + margin-left: .75em; /* space between */ + } +.fc-direction-rtl .fc-toolbar > * > :not(:first-child) { + margin-right: .75em; /* space between */ + } +.fc-direction-rtl .fc-toolbar-ltr { /* when the toolbar-chunk positioning system is explicitly left-to-right */ + flex-direction: row-reverse; + } +.fc .fc-scroller { + -webkit-overflow-scrolling: touch; + position: relative; /* for abs-positioned elements within */ + } +.fc .fc-scroller-liquid { + height: 100%; + } +.fc .fc-scroller-liquid-absolute { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + } +.fc .fc-scroller-harness { + position: relative; + overflow: hidden; + direction: ltr; + /* hack for chrome computing the scroller's right/left wrong for rtl. undone below... */ + /* TODO: demonstrate in codepen */ + } +.fc .fc-scroller-harness-liquid { + height: 100%; + } +.fc-direction-rtl .fc-scroller-harness > .fc-scroller { /* undo above hack */ + direction: rtl; + } +.fc-theme-standard .fc-scrollgrid { + border: 1px solid #ddd; + border: 1px solid var(--fc-border-color, #ddd); /* bootstrap does this. match */ + } +.fc .fc-scrollgrid, + .fc .fc-scrollgrid table { /* all tables (self included) */ + width: 100%; /* because tables don't normally do this */ + table-layout: fixed; + } +.fc .fc-scrollgrid table { /* inner tables */ + border-top-style: hidden; + border-left-style: hidden; + border-right-style: hidden; + } +.fc .fc-scrollgrid { + + border-collapse: separate; + border-right-width: 0; + border-bottom-width: 0; + + } +.fc .fc-scrollgrid-liquid { + height: 100%; + } +.fc .fc-scrollgrid-section { /* a */ + height: 1px /* better than 0, for firefox */ + + } +.fc .fc-scrollgrid-section > td { + height: 1px; /* needs a height so inner div within grow. better than 0, for firefox */ + } +.fc .fc-scrollgrid-section table { + height: 1px; + /* for most browsers, if a height isn't set on the table, can't do liquid-height within cells */ + /* serves as a min-height. harmless */ + } +.fc .fc-scrollgrid-section-liquid { + height: auto + + } +.fc .fc-scrollgrid-section-liquid > td { + height: 100%; /* better than `auto`, for firefox */ + } +.fc .fc-scrollgrid-section > * { + border-top-width: 0; + border-left-width: 0; + } +.fc .fc-scrollgrid-section-header > *, + .fc .fc-scrollgrid-section-footer > * { + border-bottom-width: 0; + } +.fc .fc-scrollgrid-section-body table, + .fc .fc-scrollgrid-section-footer table { + border-bottom-style: hidden; /* head keeps its bottom border tho */ + } +.fc { + + /* stickiness */ + +} +.fc .fc-scrollgrid-section-sticky > * { + background: #fff; + background: var(--fc-page-bg-color, #fff); + position: -webkit-sticky; + position: sticky; + z-index: 2; /* TODO: var */ + /* TODO: box-shadow when sticking */ + } +.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky > * { + top: 0; /* because border-sharing causes a gap at the top */ + /* TODO: give safari -1. has bug */ + } +.fc .fc-scrollgrid-section-footer.fc-scrollgrid-section-sticky > * { + bottom: 0; /* known bug: bottom-stickiness doesn't work in safari */ + } +.fc .fc-scrollgrid-sticky-shim { /* for horizontal scrollbar */ + height: 1px; /* needs height to create scrollbars */ + margin-bottom: -1px; + } +.fc-sticky { /* no .fc wrap because used as child of body */ + position: -webkit-sticky; + position: sticky; +} +.fc .fc-view-harness { + flex-grow: 1; /* because this harness is WITHIN the .fc's flexbox */ + position: relative; + } +.fc { + + /* when the harness controls the height, make the view liquid */ + +} +.fc .fc-view-harness-active > .fc-view { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } +.fc .fc-col-header-cell-cushion { + display: inline-block; /* x-browser for when sticky (when multi-tier header) */ + padding: 2px 4px; + } +.fc .fc-bg-event, + .fc .fc-non-business, + .fc .fc-highlight { + /* will always have a harness with position:relative/absolute, so absolutely expand */ + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +.fc .fc-non-business { + background: rgba(215, 215, 215, 0.3); + background: var(--fc-non-business-color, rgba(215, 215, 215, 0.3)); + } +.fc .fc-bg-event { + background: rgb(143, 223, 130); + background: var(--fc-bg-event-color, rgb(143, 223, 130)); + opacity: 0.3; + opacity: var(--fc-bg-event-opacity, 0.3) + } +.fc .fc-bg-event .fc-event-title { + margin: .5em; + font-size: .85em; + font-size: var(--fc-small-font-size, .85em); + font-style: italic; + } +.fc .fc-highlight { + background: rgba(188, 232, 241, 0.3); + background: var(--fc-highlight-color, rgba(188, 232, 241, 0.3)); + } +.fc .fc-cell-shaded, + .fc .fc-day-disabled { + background: rgba(208, 208, 208, 0.3); + background: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3)); + } +/* link resets */ +/* ---------------------------------------------------------------------------------------------------- */ +a.fc-event, +a.fc-event:hover { + text-decoration: none; +} +/* cursor */ +.fc-event[href], +.fc-event.fc-event-draggable { + cursor: pointer; +} +/* event text content */ +/* ---------------------------------------------------------------------------------------------------- */ +.fc-event .fc-event-main { + position: relative; + z-index: 2; + } +/* dragging */ +/* ---------------------------------------------------------------------------------------------------- */ +.fc-event-dragging:not(.fc-event-selected) { /* MOUSE */ + opacity: 0.75; + } +.fc-event-dragging.fc-event-selected { /* TOUCH */ + box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3); + } +/* resizing */ +/* ---------------------------------------------------------------------------------------------------- */ +/* (subclasses should hone positioning for touch and non-touch) */ +.fc-event .fc-event-resizer { + display: none; + position: absolute; + z-index: 4; + } +.fc-event:hover, /* MOUSE */ +.fc-event-selected { /* TOUCH */ + +} +.fc-event:hover .fc-event-resizer, .fc-event-selected .fc-event-resizer { + display: block; + } +.fc-event-selected .fc-event-resizer { + border-radius: 4px; + border-radius: calc(var(--fc-event-resizer-dot-total-width, 8px) / 2); + border-width: 1px; + border-width: var(--fc-event-resizer-dot-border-width, 1px); + width: 8px; + width: var(--fc-event-resizer-dot-total-width, 8px); + height: 8px; + height: var(--fc-event-resizer-dot-total-width, 8px); + border-style: solid; + border-color: inherit; + background: #fff; + background: var(--fc-page-bg-color, #fff) + + /* expand hit area */ + + } +.fc-event-selected .fc-event-resizer:before { + content: ''; + position: absolute; + top: -20px; + left: -20px; + right: -20px; + bottom: -20px; + } +/* selecting (always TOUCH) */ +/* ---------------------------------------------------------------------------------------------------- */ +.fc-event-selected { + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2) + + /* expand hit area (subclasses should expand) */ + +} +.fc-event-selected:before { + content: ""; + position: absolute; + z-index: 3; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +.fc-event-selected { + + /* dimmer effect */ + +} +.fc-event-selected:after { + content: ""; + background: rgba(0, 0, 0, 0.25); + background: var(--fc-event-selected-overlay-color, rgba(0, 0, 0, 0.25)); + position: absolute; + z-index: 1; + + /* assume there's a border on all sides. overcome it. */ + /* sometimes there's NOT a border, in which case the dimmer will go over */ + /* an adjacent border, which looks fine. */ + top: -1px; + left: -1px; + right: -1px; + bottom: -1px; + } +/* +A HORIZONTAL event +*/ +.fc-h-event { /* allowed to be top-level */ + display: block; + border: 1px solid #3788d8; + border: 1px solid var(--fc-event-border-color, #3788d8); + background-color: #3788d8; + background-color: var(--fc-event-bg-color, #3788d8) + +} +.fc-h-event .fc-event-main { + color: #fff; + color: var(--fc-event-text-color, #fff); + } +.fc-h-event .fc-event-main-frame { + display: flex; /* for make fc-event-title-container expand */ + } +.fc-h-event .fc-event-time { + max-width: 100%; /* clip overflow on this element */ + overflow: hidden; + } +.fc-h-event .fc-event-title-container { /* serves as a container for the sticky cushion */ + flex-grow: 1; + flex-shrink: 1; + min-width: 0; /* important for allowing to shrink all the way */ + } +.fc-h-event .fc-event-title { + display: inline-block; /* need this to be sticky cross-browser */ + vertical-align: top; /* for not messing up line-height */ + left: 0; /* for sticky */ + right: 0; /* for sticky */ + max-width: 100%; /* clip overflow on this element */ + overflow: hidden; + } +.fc-h-event.fc-event-selected:before { + /* expand hit area */ + top: -10px; + bottom: -10px; + } +/* adjust border and border-radius (if there is any) for non-start/end */ +.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start), +.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left-width: 0; +} +.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end), +.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right-width: 0; +} +/* resizers */ +.fc-h-event:not(.fc-event-selected) .fc-event-resizer { + top: 0; + bottom: 0; + width: 8px; + width: var(--fc-event-resizer-thickness, 8px); +} +.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start, +.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end { + cursor: w-resize; + left: -4px; + left: calc(var(--fc-event-resizer-thickness, 8px) / -2); +} +.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end, +.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start { + cursor: e-resize; + right: -4px; + right: calc(var(--fc-event-resizer-thickness, 8px) / -2); +} +/* resizers for TOUCH */ +.fc-h-event.fc-event-selected .fc-event-resizer { + top: 50%; + margin-top: -4px; + margin-top: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); +} +.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start, +.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end { + left: -4px; + left: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); +} +.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end, +.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start { + right: -4px; + right: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); +} + + +:root { + --fc-daygrid-event-dot-width: 8px; +} +.fc .fc-popover { + position: fixed; + top: 0; /* for when not positioned yet */ + box-shadow: 0 2px 6px rgba(0,0,0,.15); + } +.fc .fc-popover-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 3px 4px; + } +.fc .fc-popover-title { + margin: 0 2px; + } +.fc .fc-popover-close { + cursor: pointer; + opacity: 0.65; + font-size: 1.1em; + } +.fc-theme-standard .fc-popover { + border: 1px solid #ddd; + border: 1px solid var(--fc-border-color, #ddd); + background: #fff; + background: var(--fc-page-bg-color, #fff); + } +.fc-theme-standard .fc-popover-header { + background: rgba(208, 208, 208, 0.3); + background: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3)); + } +/* help things clear margins of inner content */ +.fc-daygrid-day-frame, +.fc-daygrid-day-events, +.fc-daygrid-event-harness { /* for event top/bottom margins */ +} +.fc-daygrid-day-frame:before, .fc-daygrid-day-events:before, .fc-daygrid-event-harness:before { + content: ""; + clear: both; + display: table; } +.fc-daygrid-day-frame:after, .fc-daygrid-day-events:after, .fc-daygrid-event-harness:after { + content: ""; + clear: both; + display: table; } +.fc .fc-daygrid-body { /* a
that wraps the table */ + position: relative; + z-index: 1; /* container inner z-index's because s can't do it */ + } +.fc .fc-daygrid-day.fc-day-today { + background-color: rgba(255, 220, 40, 0.15); + background-color: var(--fc-today-bg-color, rgba(255, 220, 40, 0.15)); + } +.fc .fc-daygrid-day-frame { + position: relative; + min-height: 100%; /* seems to work better than `height` because sets height after rows/cells naturally do it */ + } +.fc { + + /* cell top */ + +} +.fc .fc-daygrid-day-top { + display: flex; + flex-direction: row-reverse; + } +.fc .fc-day-other .fc-daygrid-day-top { + opacity: 0.3; + } +.fc { + + /* day number (within cell top) */ + +} +.fc .fc-daygrid-day-number { + position: relative; + z-index: 4; + padding: 4px; + } +.fc { + + /* event container */ + +} +.fc .fc-daygrid-day-events { + margin-top: 1px; /* needs to be margin, not padding, so that available cell height can be computed */ + } +.fc { + + /* positioning for balanced vs natural */ + +} +.fc .fc-daygrid-body-balanced .fc-daygrid-day-events { + position: absolute; + left: 0; + right: 0; + } +.fc .fc-daygrid-body-unbalanced .fc-daygrid-day-events { + position: relative; /* for containing abs positioned event harnesses */ + min-height: 2em; /* in addition to being a min-height during natural height, equalizes the heights a little bit */ + } +.fc .fc-daygrid-body-natural { /* can coexist with -unbalanced */ + } +.fc .fc-daygrid-body-natural .fc-daygrid-day-events { + margin-bottom: 1em; + } +.fc { + + /* event harness */ + +} +.fc .fc-daygrid-event-harness { + position: relative; + } +.fc .fc-daygrid-event-harness-abs { + position: absolute; + top: 0; /* fallback coords for when cannot yet be computed */ + left: 0; /* */ + right: 0; /* */ + } +.fc .fc-daygrid-bg-harness { + position: absolute; + top: 0; + bottom: 0; + } +.fc { + + /* bg content */ + +} +.fc .fc-daygrid-day-bg .fc-non-business { z-index: 1 } +.fc .fc-daygrid-day-bg .fc-bg-event { z-index: 2 } +.fc .fc-daygrid-day-bg .fc-highlight { z-index: 3 } +.fc { + + /* events */ + +} +.fc .fc-daygrid-event { + z-index: 6; + margin-top: 1px; + } +.fc .fc-daygrid-event.fc-event-mirror { + z-index: 7; + } +.fc { + + /* cell bottom (within day-events) */ + +} +.fc .fc-daygrid-day-bottom { + font-size: .85em; + margin: 2px 3px 0; + } +.fc .fc-daygrid-more-link { + position: relative; + z-index: 4; + cursor: pointer; + } +.fc { + + /* week number (within frame) */ + +} +.fc .fc-daygrid-week-number { + position: absolute; + z-index: 5; + top: 0; + padding: 2px; + min-width: 1.5em; + text-align: center; + background-color: rgba(208, 208, 208, 0.3); + background-color: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3)); + color: #808080; + color: var(--fc-neutral-text-color, #808080); + } +.fc { + + /* popover */ + +} +.fc .fc-more-popover { + z-index: 8; + } +.fc .fc-more-popover .fc-popover-body { + min-width: 220px; + padding: 10px; + } +.fc-direction-ltr .fc-daygrid-event.fc-event-start, +.fc-direction-rtl .fc-daygrid-event.fc-event-end { + margin-left: 2px; +} +.fc-direction-ltr .fc-daygrid-event.fc-event-end, +.fc-direction-rtl .fc-daygrid-event.fc-event-start { + margin-right: 2px; +} +.fc-direction-ltr .fc-daygrid-week-number { + left: 0; + border-radius: 0 0 3px 0; + } +.fc-direction-rtl .fc-daygrid-week-number { + right: 0; + border-radius: 0 0 0 3px; + } +.fc-liquid-hack .fc-daygrid-day-frame { + position: static; /* will cause inner absolute stuff to expand to */ + } +.fc-daygrid-event { /* make root-level, because will be dragged-and-dropped outside of a component root */ + position: relative; /* for z-indexes assigned later */ + white-space: nowrap; + border-radius: 3px; /* dot event needs this to when selected */ + font-size: .85em; + font-size: var(--fc-small-font-size, .85em); +} +/* --- the rectangle ("block") style of event --- */ +.fc-daygrid-block-event .fc-event-time { + font-weight: bold; + } +.fc-daygrid-block-event .fc-event-time, + .fc-daygrid-block-event .fc-event-title { + padding: 1px; + } +/* --- the dot style of event --- */ +.fc-daygrid-dot-event { + display: flex; + align-items: center; + padding: 2px 0 + +} +.fc-daygrid-dot-event .fc-event-title { + flex-grow: 1; + flex-shrink: 1; + min-width: 0; /* important for allowing to shrink all the way */ + overflow: hidden; + font-weight: bold; + } +.fc-daygrid-dot-event:hover, + .fc-daygrid-dot-event.fc-event-mirror { + background: rgba(0, 0, 0, 0.1); + } +.fc-daygrid-dot-event.fc-event-selected:before { + /* expand hit area */ + top: -10px; + bottom: -10px; + } +.fc-daygrid-event-dot { /* the actual dot */ + margin: 0 4px; + box-sizing: content-box; + width: 0; + height: 0; + border: 4px solid #3788d8; + border: calc(var(--fc-daygrid-event-dot-width, 8px) / 2) solid var(--fc-event-border-color, #3788d8); + border-radius: 4px; + border-radius: calc(var(--fc-daygrid-event-dot-width, 8px) / 2); +} +/* --- spacing between time and title --- */ +.fc-direction-ltr .fc-daygrid-event .fc-event-time { + margin-right: 3px; + } +.fc-direction-rtl .fc-daygrid-event .fc-event-time { + margin-left: 3px; + } + + +/* +A VERTICAL event +*/ + +.fc-v-event { /* allowed to be top-level */ + display: block; + border: 1px solid #3788d8; + border: 1px solid var(--fc-event-border-color, #3788d8); + background-color: #3788d8; + background-color: var(--fc-event-bg-color, #3788d8) + +} + +.fc-v-event .fc-event-main { + color: #fff; + color: var(--fc-event-text-color, #fff); + height: 100%; + } + +.fc-v-event .fc-event-main-frame { + height: 100%; + display: flex; + flex-direction: column; + } + +.fc-v-event .fc-event-time { + flex-grow: 0; + flex-shrink: 0; + max-height: 100%; + overflow: hidden; + } + +.fc-v-event .fc-event-title-container { /* a container for the sticky cushion */ + flex-grow: 1; + flex-shrink: 1; + min-height: 0; /* important for allowing to shrink all the way */ + } + +.fc-v-event .fc-event-title { /* will have fc-sticky on it */ + top: 0; + bottom: 0; + max-height: 100%; /* clip overflow */ + overflow: hidden; + } + +.fc-v-event:not(.fc-event-start) { + border-top-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + +.fc-v-event:not(.fc-event-end) { + border-bottom-width: 0; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + +.fc-v-event.fc-event-selected:before { + /* expand hit area */ + left: -10px; + right: -10px; + } + +.fc-v-event { + + /* resizer (mouse AND touch) */ + +} + +.fc-v-event .fc-event-resizer-start { + cursor: n-resize; + } + +.fc-v-event .fc-event-resizer-end { + cursor: s-resize; + } + +.fc-v-event { + + /* resizer for MOUSE */ + +} + +.fc-v-event:not(.fc-event-selected) .fc-event-resizer { + height: 8px; + height: var(--fc-event-resizer-thickness, 8px); + left: 0; + right: 0; + } + +.fc-v-event:not(.fc-event-selected) .fc-event-resizer-start { + top: -4px; + top: calc(var(--fc-event-resizer-thickness, 8px) / -2); + } + +.fc-v-event:not(.fc-event-selected) .fc-event-resizer-end { + bottom: -4px; + bottom: calc(var(--fc-event-resizer-thickness, 8px) / -2); + } + +.fc-v-event { + + /* resizer for TOUCH (when event is "selected") */ + +} + +.fc-v-event.fc-event-selected .fc-event-resizer { + left: 50%; + margin-left: -4px; + margin-left: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); + } + +.fc-v-event.fc-event-selected .fc-event-resizer-start { + top: -4px; + top: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); + } + +.fc-v-event.fc-event-selected .fc-event-resizer-end { + bottom: -4px; + bottom: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2); + } +.fc .fc-timegrid .fc-daygrid-body { /* the all-day daygrid within the timegrid view */ + z-index: 2; /* put above the timegrid-body so that more-popover is above everything. TODO: better solution */ + } +.fc .fc-timegrid-divider { + padding: 0 0 2px; /* browsers get confused when you set height. use padding instead */ + } +.fc .fc-timegrid-body { + position: relative; + z-index: 1; /* scope the z-indexes of slots and cols */ + min-height: 100%; /* fill height always, even when slat table doesn't grow */ + } +.fc .fc-timegrid-axis-chunk { /* for advanced ScrollGrid */ + position: relative /* offset parent for now-indicator-container */ + + } +.fc .fc-timegrid-axis-chunk > table { + position: relative; + z-index: 1; /* above the now-indicator-container */ + } +.fc .fc-timegrid-slots { + position: relative; + z-index: 1; + } +.fc .fc-timegrid-slot { /* a */ + height: 1.5em; + border-bottom: 0 /* each cell owns its top border */ + } +.fc .fc-timegrid-slot:empty:before { + content: '\00a0'; /* make sure there's at least an empty space to create height for height syncing */ + } +.fc .fc-timegrid-slot-minor { + border-top-style: dotted; + } +.fc .fc-timegrid-slot-label-cushion { + display: inline-block; + white-space: nowrap; + } +.fc .fc-timegrid-slot-label { + vertical-align: middle; /* vertical align the slots */ + } +.fc { + + + /* slots AND axis cells (top-left corner of view including the "all-day" text) */ + +} +.fc .fc-timegrid-axis-cushion, + .fc .fc-timegrid-slot-label-cushion { + padding: 0 4px; + } +.fc { + + + /* axis cells (top-left corner of view including the "all-day" text) */ + /* vertical align is more complicated, uses flexbox */ + +} +.fc .fc-timegrid-axis-frame-liquid { + height: 100%; /* will need liquid-hack in FF */ + } +.fc .fc-timegrid-axis-frame { + overflow: hidden; + display: flex; + align-items: center; /* vertical align */ + justify-content: flex-end; /* horizontal align. matches text-align below */ + } +.fc .fc-timegrid-axis-cushion { + max-width: 60px; /* limits the width of the "all-day" text */ + flex-shrink: 0; /* allows text to expand how it normally would, regardless of constrained width */ + } +.fc-direction-ltr .fc-timegrid-slot-label-frame { + text-align: right; + } +.fc-direction-rtl .fc-timegrid-slot-label-frame { + text-align: left; + } +.fc-liquid-hack .fc-timegrid-axis-frame-liquid { + height: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } +.fc .fc-timegrid-col.fc-day-today { + background-color: rgba(255, 220, 40, 0.15); + background-color: var(--fc-today-bg-color, rgba(255, 220, 40, 0.15)); + } +.fc .fc-timegrid-col-frame { + min-height: 100%; /* liquid-hack is below */ + position: relative; + } +.fc-liquid-hack .fc-timegrid-col-frame { + height: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } +.fc-media-screen .fc-timegrid-cols { + position: absolute; /* no z-index. children will decide and go above slots */ + top: 0; + left: 0; + right: 0; + bottom: 0 + } +.fc-media-screen .fc-timegrid-cols > table { + height: 100%; + } +.fc-media-screen .fc-timegrid-col-bg, + .fc-media-screen .fc-timegrid-col-events, + .fc-media-screen .fc-timegrid-now-indicator-container { + position: absolute; + top: 0; + left: 0; + right: 0; + } +.fc-media-screen .fc-timegrid-event-harness { + position: absolute; /* top/left/right/bottom will all be set by JS */ + } +.fc { + + /* bg */ + +} +.fc .fc-timegrid-col-bg { + z-index: 2; /* TODO: kill */ + } +.fc .fc-timegrid-col-bg .fc-non-business { z-index: 1 } +.fc .fc-timegrid-col-bg .fc-bg-event { z-index: 2 } +.fc .fc-timegrid-col-bg .fc-highlight { z-index: 3 } +.fc .fc-timegrid-bg-harness { + position: absolute; /* top/bottom will be set by JS */ + left: 0; + right: 0; + } +.fc { + + /* fg events */ + /* (the mirror segs are put into a separate container with same classname, */ + /* and they must be after the normal seg container to appear at a higher z-index) */ + +} +.fc .fc-timegrid-col-events { + z-index: 3; + /* child event segs have z-indexes that are scoped within this div */ + } +.fc { + + /* now indicator */ + +} +.fc .fc-timegrid-now-indicator-container { + bottom: 0; + overflow: hidden; /* don't let overflow of lines/arrows cause unnecessary scrolling */ + /* z-index is set on the individual elements */ + } +.fc-direction-ltr .fc-timegrid-col-events { + margin: 0 2.5% 0 2px; + } +.fc-direction-rtl .fc-timegrid-col-events { + margin: 0 2px 0 2.5%; + } +.fc-timegrid-event-harness-inset .fc-timegrid-event, +.fc-timegrid-event.fc-event-mirror { + box-shadow: 0px 0px 0px 1px #fff; + box-shadow: 0px 0px 0px 1px var(--fc-page-bg-color, #fff); +} +.fc-timegrid-event { /* events need to be root */ + + font-size: .85em; + + font-size: var(--fc-small-font-size, .85em); + border-radius: 3px + +} +.fc-timegrid-event .fc-event-main { + padding: 1px 1px 0; + } +.fc-timegrid-event .fc-event-time { + white-space: nowrap; + font-size: .85em; + font-size: var(--fc-small-font-size, .85em); + margin-bottom: 1px; + } +.fc-timegrid-event-condensed .fc-event-main-frame { + flex-direction: row; + overflow: hidden; + } +.fc-timegrid-event-condensed .fc-event-time:after { + content: '\00a0-\00a0'; /* dash surrounded by non-breaking spaces */ + } +.fc-timegrid-event-condensed .fc-event-title { + font-size: .85em; + font-size: var(--fc-small-font-size, .85em) + } +.fc-media-screen .fc-timegrid-event { + position: absolute; /* absolute WITHIN the harness */ + top: 0; + bottom: 1px; /* stay away from bottom slot line */ + left: 0; + right: 0; + } +.fc { + + /* line */ + +} +.fc .fc-timegrid-now-indicator-line { + position: absolute; + z-index: 4; + left: 0; + right: 0; + border-style: solid; + border-color: red; + border-color: var(--fc-now-indicator-color, red); + border-width: 1px 0 0; + } +.fc { + + /* arrow */ + +} +.fc .fc-timegrid-now-indicator-arrow { + position: absolute; + z-index: 4; + margin-top: -5px; /* vertically center on top coordinate */ + border-style: solid; + border-color: red; + border-color: var(--fc-now-indicator-color, red); + } +.fc-direction-ltr .fc-timegrid-now-indicator-arrow { + left: 0; + + /* triangle pointing right. TODO: mixin */ + border-width: 5px 0 5px 6px; + border-top-color: transparent; + border-bottom-color: transparent; + } +.fc-direction-rtl .fc-timegrid-now-indicator-arrow { + right: 0; + + /* triangle pointing left. TODO: mixin */ + border-width: 5px 6px 5px 0; + border-top-color: transparent; + border-bottom-color: transparent; + } + + +:root { + --fc-list-event-dot-width: 10px; + --fc-list-event-hover-bg-color: #f5f5f5; +} +.fc-theme-standard .fc-list { + border: 1px solid #ddd; + border: 1px solid var(--fc-border-color, #ddd); + } +.fc { + + /* message when no events */ + +} +.fc .fc-list-empty { + background-color: rgba(208, 208, 208, 0.3); + background-color: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3)); + height: 100%; + display: flex; + justify-content: center; + align-items: center; /* vertically aligns fc-list-empty-inner */ + } +.fc .fc-list-empty-cushion { + margin: 5em 0; + } +.fc { + + /* table within the scroller */ + /* ---------------------------------------------------------------------------------------------------- */ + +} +.fc .fc-list-table { + width: 100%; + border-style: hidden; /* kill outer border on theme */ + } +.fc .fc-list-table tr > * { + border-left: 0; + border-right: 0; + } +.fc .fc-list-sticky .fc-list-day > * { /* the cells */ + position: -webkit-sticky; + position: sticky; + top: 0; + background: #fff; + background: var(--fc-page-bg-color, #fff); /* for when headers are styled to be transparent and sticky */ + } +.fc .fc-list-table th { + padding: 0; /* uses an inner-wrapper instead... */ + } +.fc .fc-list-table td, + .fc .fc-list-day-cushion { + padding: 8px 14px; + } +.fc { + + + /* date heading rows */ + /* ---------------------------------------------------------------------------------------------------- */ + +} +.fc .fc-list-day-cushion:after { + content: ""; + clear: both; + display: table; /* clear floating */ + } +.fc-theme-standard .fc-list-day-cushion { + background-color: rgba(208, 208, 208, 0.3); + background-color: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3)); + } +.fc-direction-ltr .fc-list-day-text, +.fc-direction-rtl .fc-list-day-side-text { + float: left; +} +.fc-direction-ltr .fc-list-day-side-text, +.fc-direction-rtl .fc-list-day-text { + float: right; +} +/* make the dot closer to the event title */ +.fc-direction-ltr .fc-list-table .fc-list-event-graphic { padding-right: 0 } +.fc-direction-rtl .fc-list-table .fc-list-event-graphic { padding-left: 0 } +.fc .fc-list-event.fc-event-forced-url { + cursor: pointer; /* whole row will seem clickable */ + } +.fc .fc-list-event:hover td { + background-color: #f5f5f5; + background-color: var(--fc-list-event-hover-bg-color, #f5f5f5); + } +.fc { + + /* shrink certain cols */ + +} +.fc .fc-list-event-graphic, + .fc .fc-list-event-time { + white-space: nowrap; + width: 1px; + } +.fc .fc-list-event-dot { + display: inline-block; + box-sizing: content-box; + width: 0; + height: 0; + border: 5px solid #3788d8; + border: calc(var(--fc-list-event-dot-width, 10px) / 2) solid var(--fc-event-border-color, #3788d8); + border-radius: 5px; + border-radius: calc(var(--fc-list-event-dot-width, 10px) / 2); + } +.fc { + + /* reset styling */ + +} +.fc .fc-list-event-title a { + color: inherit; + text-decoration: none; + } +.fc { + + /* underline link when hovering over any part of row */ + +} +.fc .fc-list-event.fc-event-forced-url:hover a { + text-decoration: underline; + } + + + + .fc-theme-bootstrap a:not([href]) { + color: inherit; /* natural color for navlinks */ + } + diff --git a/static/fullcalendar/main.js b/static/fullcalendar/main.js new file mode 100644 index 0000000..bdb6e20 --- /dev/null +++ b/static/fullcalendar/main.js @@ -0,0 +1,14420 @@ +/*! +FullCalendar v5.3.2 +Docs & License: https://fullcalendar.io/ +(c) 2020 Adam Shaw +*/ +var FullCalendar = (function (exports) { + 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; + } + + var n,u,i,t,r,o,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function v(n){var l=n.parentNode;l&&l.removeChild(n);}function h(n,l,u){var i,t=arguments,r={};for(i in l)"key"!==i&&"ref"!==i&&(r[i]=l[i]);if(arguments.length>3)for(u=[u],i=3;i= 1) { + return Math.min(w, nextW); + } + return w; + } + function weekOfGivenYear(marker, year, dow, doy) { + var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]); + var dayStart = startOfDay(marker); + var days = Math.round(diffDays(firstWeekStart, dayStart)); + return Math.floor(days / 7) + 1; // zero-indexed + } + // start-of-first-week - start-of-year + function firstWeekOffset(year, dow, doy) { + // first-week day -- which january is always in the first week (4 for iso, 1 for other) + var fwd = 7 + dow - doy; + // first-week day local weekday -- which local weekday is fwd + var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7; + return -fwdlw + fwd - 1; + } + // Array Conversion + function dateToLocalArray(date) { + return [ + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + date.getMilliseconds() + ]; + } + function arrayToLocalDate(a) { + return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month + a[3] || 0, a[4] || 0, a[5] || 0); + } + function dateToUtcArray(date) { + return [ + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + date.getUTCMilliseconds() + ]; + } + function arrayToUtcDate(a) { + // according to web standards (and Safari), a month index is required. + // massage if only given a year. + if (a.length === 1) { + a = a.concat([0]); + } + return new Date(Date.UTC.apply(Date, a)); + } + // Other Utils + function isValidDate(m) { + return !isNaN(m.valueOf()); + } + function timeAsMs(m) { + return m.getUTCHours() * 1000 * 60 * 60 + + m.getUTCMinutes() * 1000 * 60 + + m.getUTCSeconds() * 1000 + + m.getUTCMilliseconds(); + } + + function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) { + return { + instanceId: guid(), + defId: defId, + range: range, + forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo, + forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo + }; + } + + var hasOwnProperty = Object.prototype.hasOwnProperty; + // Merges an array of objects into a single object. + // The second argument allows for an array of property names who's object values will be merged together. + function mergeProps(propObjs, complexPropsMap) { + var dest = {}; + if (complexPropsMap) { + for (var name_1 in complexPropsMap) { + var complexObjs = []; + // collect the trailing object values, stopping when a non-object is discovered + for (var i = propObjs.length - 1; i >= 0; i--) { + var val = propObjs[i][name_1]; + if (typeof val === 'object' && val) { // non-null object + complexObjs.unshift(val); + } + else if (val !== undefined) { + dest[name_1] = val; // if there were no objects, this value will be used + break; + } + } + // if the trailing values were objects, use the merged value + if (complexObjs.length) { + dest[name_1] = mergeProps(complexObjs); + } + } + } + // copy values into the destination, going from last to first + for (var i = propObjs.length - 1; i >= 0; i--) { + var props = propObjs[i]; + for (var name_2 in props) { + if (!(name_2 in dest)) { // if already assigned by previous props or complex props, don't reassign + dest[name_2] = props[name_2]; + } + } + } + return dest; + } + function filterHash(hash, func) { + var filtered = {}; + for (var key in hash) { + if (func(hash[key], key)) { + filtered[key] = hash[key]; + } + } + return filtered; + } + function mapHash(hash, func) { + var newHash = {}; + for (var key in hash) { + newHash[key] = func(hash[key], key); + } + return newHash; + } + function arrayToHash(a) { + var hash = {}; + for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { + var item = a_1[_i]; + hash[item] = true; + } + return hash; + } + function buildHashFromArray(a, func) { + var hash = {}; + for (var i = 0; i < a.length; i++) { + var tuple = func(a[i], i); + hash[tuple[0]] = tuple[1]; + } + return hash; + } + function hashValuesToArray(obj) { + var a = []; + for (var key in obj) { + a.push(obj[key]); + } + return a; + } + function isPropsEqual(obj0, obj1) { + if (obj0 === obj1) { + return true; + } + for (var key in obj0) { + if (hasOwnProperty.call(obj0, key)) { + if (!(key in obj1)) { + return false; + } + } + } + for (var key in obj1) { + if (hasOwnProperty.call(obj1, key)) { + if (obj0[key] !== obj1[key]) { + return false; + } + } + } + return true; + } + function getUnequalProps(obj0, obj1) { + var keys = []; + for (var key in obj0) { + if (hasOwnProperty.call(obj0, key)) { + if (!(key in obj1)) { + keys.push(key); + } + } + } + for (var key in obj1) { + if (hasOwnProperty.call(obj1, key)) { + if (obj0[key] !== obj1[key]) { + keys.push(key); + } + } + } + return keys; + } + function compareObjs(oldProps, newProps, equalityFuncs) { + if (equalityFuncs === void 0) { equalityFuncs = {}; } + if (oldProps === newProps) { + return true; + } + for (var key in newProps) { + if (key in oldProps && isObjValsEqual(oldProps[key], newProps[key], equalityFuncs[key])) ; + else { + return false; + } + } + // check for props that were omitted in the new + for (var key in oldProps) { + if (!(key in newProps)) { + return false; + } + } + return true; + } + /* + assumed "true" equality for handler names like "onReceiveSomething" + */ + function isObjValsEqual(val0, val1, comparator) { + if (val0 === val1 || comparator === true) { + return true; + } + if (comparator) { + return comparator(val0, val1); + } + return false; + } + function collectFromHash(hash, startIndex, endIndex, step) { + if (startIndex === void 0) { startIndex = 0; } + if (step === void 0) { step = 1; } + var res = []; + if (endIndex == null) { + endIndex = Object.keys(hash).length; + } + for (var i = startIndex; i < endIndex; i += step) { + var val = hash[i]; + if (val !== undefined) { // will disregard undefined for sparse arrays + res.push(val); + } + } + return res; + } + + function parseRecurring(refined, defaultAllDay, dateEnv, recurringTypes) { + for (var i = 0; i < recurringTypes.length; i++) { + var parsed = recurringTypes[i].parse(refined, dateEnv); + if (parsed) { + var allDay = refined.allDay; + if (allDay == null) { + allDay = defaultAllDay; + if (allDay == null) { + allDay = parsed.allDayGuess; + if (allDay == null) { + allDay = false; + } + } + } + return { + allDay: allDay, + duration: parsed.duration, + typeData: parsed.typeData, + typeId: i + }; + } + } + return null; + } + function expandRecurring(eventStore, framingRange, context) { + var dateEnv = context.dateEnv, pluginHooks = context.pluginHooks, options = context.options; + var defs = eventStore.defs, instances = eventStore.instances; + // remove existing recurring instances + // TODO: bad. always expand events as a second step + instances = filterHash(instances, function (instance) { + return !defs[instance.defId].recurringDef; + }); + for (var defId in defs) { + var def = defs[defId]; + if (def.recurringDef) { + var duration = def.recurringDef.duration; + if (!duration) { + duration = def.allDay ? + options.defaultAllDayEventDuration : + options.defaultTimedEventDuration; + } + var starts = expandRecurringRanges(def, duration, framingRange, dateEnv, pluginHooks.recurringTypes); + for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) { + var start = starts_1[_i]; + var instance = createEventInstance(defId, { + start: start, + end: dateEnv.add(start, duration) + }); + instances[instance.instanceId] = instance; + } + } + } + return { defs: defs, instances: instances }; + } + /* + Event MUST have a recurringDef + */ + function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) { + var typeDef = recurringTypes[eventDef.recurringDef.typeId]; + var markers = typeDef.expand(eventDef.recurringDef.typeData, { + start: dateEnv.subtract(framingRange.start, duration), + end: framingRange.end + }, dateEnv); + // the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to + if (eventDef.allDay) { + markers = markers.map(startOfDay); + } + return markers; + } + + var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds']; + var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/; + // Parsing and Creation + function createDuration(input, unit) { + var _a; + if (typeof input === 'string') { + return parseString(input); + } + else if (typeof input === 'object' && input) { // non-null object + return parseObject(input); + } + else if (typeof input === 'number') { + return parseObject((_a = {}, _a[unit || 'milliseconds'] = input, _a)); + } + else { + return null; + } + } + function parseString(s) { + var m = PARSE_RE.exec(s); + if (m) { + var sign = m[1] ? -1 : 1; + return { + years: 0, + months: 0, + days: sign * (m[2] ? parseInt(m[2], 10) : 0), + milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours + (m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes + (m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds + (m[6] ? parseInt(m[6], 10) : 0) // ms + ) + }; + } + return null; + } + function parseObject(obj) { + var duration = { + years: obj.years || obj.year || 0, + months: obj.months || obj.month || 0, + days: obj.days || obj.day || 0, + milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours + (obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes + (obj.seconds || obj.second || 0) * 1000 + // seconds + (obj.milliseconds || obj.millisecond || obj.ms || 0) // ms + }; + var weeks = obj.weeks || obj.week; + if (weeks) { + duration.days += weeks * 7; + duration.specifiedWeeks = true; + } + return duration; + } + // Equality + function durationsEqual(d0, d1) { + return d0.years === d1.years && + d0.months === d1.months && + d0.days === d1.days && + d0.milliseconds === d1.milliseconds; + } + function asCleanDays(dur) { + if (!dur.years && !dur.months && !dur.milliseconds) { + return dur.days; + } + return 0; + } + // Simple Math + function addDurations(d0, d1) { + return { + years: d0.years + d1.years, + months: d0.months + d1.months, + days: d0.days + d1.days, + milliseconds: d0.milliseconds + d1.milliseconds + }; + } + function subtractDurations(d1, d0) { + return { + years: d1.years - d0.years, + months: d1.months - d0.months, + days: d1.days - d0.days, + milliseconds: d1.milliseconds - d0.milliseconds + }; + } + function multiplyDuration(d, n) { + return { + years: d.years * n, + months: d.months * n, + days: d.days * n, + milliseconds: d.milliseconds * n + }; + } + // Conversions + // "Rough" because they are based on average-case Gregorian months/years + function asRoughYears(dur) { + return asRoughDays(dur) / 365; + } + function asRoughMonths(dur) { + return asRoughDays(dur) / 30; + } + function asRoughDays(dur) { + return asRoughMs(dur) / 864e5; + } + function asRoughMinutes(dur) { + return asRoughMs(dur) / (1000 * 60); + } + function asRoughSeconds(dur) { + return asRoughMs(dur) / 1000; + } + function asRoughMs(dur) { + return dur.years * (365 * 864e5) + + dur.months * (30 * 864e5) + + dur.days * 864e5 + + dur.milliseconds; + } + // Advanced Math + function wholeDivideDurations(numerator, denominator) { + var res = null; + for (var i = 0; i < INTERNAL_UNITS.length; i++) { + var unit = INTERNAL_UNITS[i]; + if (denominator[unit]) { + var localRes = numerator[unit] / denominator[unit]; + if (!isInt(localRes) || (res !== null && res !== localRes)) { + return null; + } + res = localRes; + } + else if (numerator[unit]) { + // needs to divide by something but can't! + return null; + } + } + return res; + } + function greatestDurationDenominator(dur) { + var ms = dur.milliseconds; + if (ms) { + if (ms % 1000 !== 0) { + return { unit: 'millisecond', value: ms }; + } + if (ms % (1000 * 60) !== 0) { + return { unit: 'second', value: ms / 1000 }; + } + if (ms % (1000 * 60 * 60) !== 0) { + return { unit: 'minute', value: ms / (1000 * 60) }; + } + if (ms) { + return { unit: 'hour', value: ms / (1000 * 60 * 60) }; + } + } + if (dur.days) { + if (dur.specifiedWeeks && dur.days % 7 === 0) { + return { unit: 'week', value: dur.days / 7 }; + } + return { unit: 'day', value: dur.days }; + } + if (dur.months) { + return { unit: 'month', value: dur.months }; + } + if (dur.years) { + return { unit: 'year', value: dur.years }; + } + return { unit: 'millisecond', value: 0 }; + } + + // timeZoneOffset is in minutes + function buildIsoString(marker, timeZoneOffset, stripZeroTime) { + if (stripZeroTime === void 0) { stripZeroTime = false; } + var s = marker.toISOString(); + s = s.replace('.000', ''); + if (stripZeroTime) { + s = s.replace('T00:00:00Z', ''); + } + if (s.length > 10) { // time part wasn't stripped, can add timezone info + if (timeZoneOffset == null) { + s = s.replace('Z', ''); + } + else if (timeZoneOffset !== 0) { + s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true)); + } + // otherwise, its UTC-0 and we want to keep the Z + } + return s; + } + // formats the date, but with no time part + // TODO: somehow merge with buildIsoString and stripZeroTime + // TODO: rename. omit "string" + function formatDayString(marker) { + return marker.toISOString().replace(/T.*$/, ''); + } + // TODO: use Date::toISOString and use everything after the T? + function formatIsoTimeString(marker) { + return padStart(marker.getUTCHours(), 2) + ':' + + padStart(marker.getUTCMinutes(), 2) + ':' + + padStart(marker.getUTCSeconds(), 2); + } + function formatTimeZoneOffset(minutes, doIso) { + if (doIso === void 0) { doIso = false; } + var sign = minutes < 0 ? '-' : '+'; + var abs = Math.abs(minutes); + var hours = Math.floor(abs / 60); + var mins = Math.round(abs % 60); + if (doIso) { + return sign + padStart(hours, 2) + ':' + padStart(mins, 2); + } + else { + return 'GMT' + sign + hours + (mins ? ':' + padStart(mins, 2) : ''); + } + } + + // TODO: new util arrayify? + function removeExact(array, exactVal) { + var removeCnt = 0; + var i = 0; + while (i < array.length) { + if (array[i] === exactVal) { + array.splice(i, 1); + removeCnt++; + } + else { + i++; + } + } + return removeCnt; + } + function isArraysEqual(a0, a1, equalityFunc) { + if (a0 === a1) { + return true; + } + var len = a0.length; + var i; + if (len !== a1.length) { // not array? or not same length? + return false; + } + for (i = 0; i < len; i++) { + if (!(equalityFunc ? equalityFunc(a0[i], a1[i]) : a0[i] === a1[i])) { + return false; + } + } + return true; + } + + function memoize(workerFunc, resEquality, teardownFunc) { + var currentArgs; + var currentRes; + return function () { + var newArgs = []; + for (var _i = 0; _i < arguments.length; _i++) { + newArgs[_i] = arguments[_i]; + } + if (!currentArgs) { + currentRes = workerFunc.apply(this, newArgs); + } + else if (!isArraysEqual(currentArgs, newArgs)) { + if (teardownFunc) { + teardownFunc(currentRes); + } + var res = workerFunc.apply(this, newArgs); + if (!resEquality || !resEquality(res, currentRes)) { + currentRes = res; + } + } + currentArgs = newArgs; + return currentRes; + }; + } + function memoizeObjArg(workerFunc, resEquality, teardownFunc) { + var currentArg; + var currentRes; + return function (newArg) { + if (!currentArg) { + currentRes = workerFunc.call(this, newArg); + } + else if (!isPropsEqual(currentArg, newArg)) { + if (teardownFunc) { + teardownFunc(currentRes); + } + var res = workerFunc.call(this, newArg); + if (!resEquality || !resEquality(res, currentRes)) { + currentRes = res; + } + } + currentArg = newArg; + return currentRes; + }; + } + function memoizeArraylike(// used at all? + workerFunc, resEquality, teardownFunc) { + var currentArgSets = []; + var currentResults = []; + return function (newArgSets) { + var currentLen = currentArgSets.length; + var newLen = newArgSets.length; + var i = 0; + for (; i < currentLen; i++) { + if (!newArgSets[i]) { // one of the old sets no longer exists + if (teardownFunc) { + teardownFunc(currentResults[i]); + } + } + else if (!isArraysEqual(currentArgSets[i], newArgSets[i])) { + if (teardownFunc) { + teardownFunc(currentResults[i]); + } + var res = workerFunc.apply(this, newArgSets[i]); + if (!resEquality || !resEquality(res, currentResults[i])) { + currentResults[i] = res; + } + } + } + for (; i < newLen; i++) { + currentResults[i] = workerFunc.apply(this, newArgSets[i]); + } + currentArgSets = newArgSets; + currentResults.splice(newLen); // remove excess + return currentResults; + }; + } + function memoizeHashlike(// used? + workerFunc, resEquality, teardownFunc // TODO: change arg order + ) { + var currentArgHash = {}; + var currentResHash = {}; + return function (newArgHash) { + var newResHash = {}; + for (var key in newArgHash) { + if (!currentResHash[key]) { + newResHash[key] = workerFunc.apply(this, newArgHash[key]); + } + else if (!isArraysEqual(currentArgHash[key], newArgHash[key])) { + if (teardownFunc) { + teardownFunc(currentResHash[key]); + } + var res = workerFunc.apply(this, newArgHash[key]); + newResHash[key] = (resEquality && resEquality(res, currentResHash[key])) + ? currentResHash[key] + : res; + } + else { + newResHash[key] = currentResHash[key]; + } + } + currentArgHash = newArgHash; + currentResHash = newResHash; + return newResHash; + }; + } + + var EXTENDED_SETTINGS_AND_SEVERITIES = { + week: 3, + separator: 0, + omitZeroMinute: 0, + meridiem: 0, + omitCommas: 0 + }; + var STANDARD_DATE_PROP_SEVERITIES = { + timeZoneName: 7, + era: 6, + year: 5, + month: 4, + day: 2, + weekday: 2, + hour: 1, + minute: 1, + second: 1 + }; + var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too + var COMMA_RE = /,/g; // we need re for globalness + var MULTI_SPACE_RE = /\s+/g; + var LTR_RE = /\u200e/g; // control character + var UTC_RE = /UTC|GMT/; + var NativeFormatter = /** @class */ (function () { + function NativeFormatter(formatSettings) { + var standardDateProps = {}; + var extendedSettings = {}; + var severity = 0; + for (var name_1 in formatSettings) { + if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) { + extendedSettings[name_1] = formatSettings[name_1]; + severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity); + } + else { + standardDateProps[name_1] = formatSettings[name_1]; + if (name_1 in STANDARD_DATE_PROP_SEVERITIES) { // TODO: what about hour12? no severity + severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity); + } + } + } + this.standardDateProps = standardDateProps; + this.extendedSettings = extendedSettings; + this.severity = severity; + this.buildFormattingFunc = memoize(buildFormattingFunc); + } + NativeFormatter.prototype.format = function (date, context) { + return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date); + }; + NativeFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { + var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings; + var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem); + if (!diffSeverity) { + return this.format(start, context); + } + var biggestUnitForPartial = diffSeverity; + if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time + (standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') && + (standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') && + (standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) { + biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time + } + var full0 = this.format(start, context); + var full1 = this.format(end, context); + if (full0 === full1) { + return full0; + } + var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial); + var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context); + var partial0 = partialFormattingFunc(start); + var partial1 = partialFormattingFunc(end); + var insertion = findCommonInsertion(full0, partial0, full1, partial1); + var separator = extendedSettings.separator || betterDefaultSeparator || context.defaultSeparator || ''; + if (insertion) { + return insertion.before + partial0 + separator + partial1 + insertion.after; + } + return full0 + separator + full1; + }; + NativeFormatter.prototype.getLargestUnit = function () { + switch (this.severity) { + case 7: + case 6: + case 5: + return 'year'; + case 4: + return 'month'; + case 3: + return 'week'; + case 2: + return 'day'; + default: + return 'time'; // really? + } + }; + return NativeFormatter; + }()); + function buildFormattingFunc(standardDateProps, extendedSettings, context) { + var standardDatePropCnt = Object.keys(standardDateProps).length; + if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') { + return function (date) { + return formatTimeZoneOffset(date.timeZoneOffset); + }; + } + if (standardDatePropCnt === 0 && extendedSettings.week) { + return function (date) { + return formatWeekNumber(context.computeWeekNumber(date.marker), context.weekText, context.locale, extendedSettings.week); + }; + } + return buildNativeFormattingFunc(standardDateProps, extendedSettings, context); + } + function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) { + standardDateProps = __assign({}, standardDateProps); // copy + extendedSettings = __assign({}, extendedSettings); // copy + sanitizeSettings(standardDateProps, extendedSettings); + standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers + var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps); + var zeroFormat; // needed? + if (extendedSettings.omitZeroMinute) { + var zeroProps = __assign({}, standardDateProps); + delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings + zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps); + } + return function (date) { + var marker = date.marker; + var format; + if (zeroFormat && !marker.getUTCMinutes()) { + format = zeroFormat; + } + else { + format = normalFormat; + } + var s = format.format(marker); + return postProcess(s, date, standardDateProps, extendedSettings, context); + }; + } + function sanitizeSettings(standardDateProps, extendedSettings) { + // deal with a browser inconsistency where formatting the timezone + // requires that the hour/minute be present. + if (standardDateProps.timeZoneName) { + if (!standardDateProps.hour) { + standardDateProps.hour = '2-digit'; + } + if (!standardDateProps.minute) { + standardDateProps.minute = '2-digit'; + } + } + // only support short timezone names + if (standardDateProps.timeZoneName === 'long') { + standardDateProps.timeZoneName = 'short'; + } + // if requesting to display seconds, MUST display minutes + if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) { + delete extendedSettings.omitZeroMinute; + } + } + function postProcess(s, date, standardDateProps, extendedSettings, context) { + s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes + if (standardDateProps.timeZoneName === 'short') { + s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ? + 'UTC' : // important to normalize for IE, which does "GMT" + formatTimeZoneOffset(date.timeZoneOffset)); + } + if (extendedSettings.omitCommas) { + s = s.replace(COMMA_RE, '').trim(); + } + if (extendedSettings.omitZeroMinute) { + s = s.replace(':00', ''); // zeroFormat doesn't always achieve this + } + // ^ do anything that might create adjacent spaces before this point, + // because MERIDIEM_RE likes to eat up loading spaces + if (extendedSettings.meridiem === false) { + s = s.replace(MERIDIEM_RE, '').trim(); + } + else if (extendedSettings.meridiem === 'narrow') { // a/p + s = s.replace(MERIDIEM_RE, function (m0, m1) { + return m1.toLocaleLowerCase(); + }); + } + else if (extendedSettings.meridiem === 'short') { // am/pm + s = s.replace(MERIDIEM_RE, function (m0, m1) { + return m1.toLocaleLowerCase() + 'm'; + }); + } + else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase + s = s.replace(MERIDIEM_RE, function (m0) { + return m0.toLocaleLowerCase(); + }); + } + s = s.replace(MULTI_SPACE_RE, ' '); + s = s.trim(); + return s; + } + function injectTzoStr(s, tzoStr) { + var replaced = false; + s = s.replace(UTC_RE, function () { + replaced = true; + return tzoStr; + }); + // IE11 doesn't include UTC/GMT in the original string, so append to end + if (!replaced) { + s += ' ' + tzoStr; + } + return s; + } + function formatWeekNumber(num, weekText, locale, display) { + var parts = []; + if (display === 'narrow') { + parts.push(weekText); + } + else if (display === 'short') { + parts.push(weekText, ' '); + } + // otherwise, considered 'numeric' + parts.push(locale.simpleNumberFormat.format(num)); + if (locale.options.direction === 'rtl') { // TODO: use control characters instead? + parts.reverse(); + } + return parts.join(''); + } + // Range Formatting Utils + // 0 = exactly the same + // 1 = different by time + // and bigger + function computeMarkerDiffSeverity(d0, d1, ca) { + if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) { + return 5; + } + if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) { + return 4; + } + if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) { + return 2; + } + if (timeAsMs(d0) !== timeAsMs(d1)) { + return 1; + } + return 0; + } + function computePartialFormattingOptions(options, biggestUnit) { + var partialOptions = {}; + for (var name_2 in options) { + if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone) + STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) { + partialOptions[name_2] = options[name_2]; + } + } + return partialOptions; + } + function findCommonInsertion(full0, partial0, full1, partial1) { + var i0 = 0; + while (i0 < full0.length) { + var found0 = full0.indexOf(partial0, i0); + if (found0 === -1) { + break; + } + var before0 = full0.substr(0, found0); + i0 = found0 + partial0.length; + var after0 = full0.substr(i0); + var i1 = 0; + while (i1 < full1.length) { + var found1 = full1.indexOf(partial1, i1); + if (found1 === -1) { + break; + } + var before1 = full1.substr(0, found1); + i1 = found1 + partial1.length; + var after1 = full1.substr(i1); + if (before0 === before1 && after0 === after1) { + return { + before: before0, + after: after0 + }; + } + } + } + return null; + } + + function expandZonedMarker(dateInfo, calendarSystem) { + var a = calendarSystem.markerToArray(dateInfo.marker); + return { + marker: dateInfo.marker, + timeZoneOffset: dateInfo.timeZoneOffset, + array: a, + year: a[0], + month: a[1], + day: a[2], + hour: a[3], + minute: a[4], + second: a[5], + millisecond: a[6] + }; + } + + function createVerboseFormattingArg(start, end, context, betterDefaultSeparator) { + var startInfo = expandZonedMarker(start, context.calendarSystem); + var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null; + return { + date: startInfo, + start: startInfo, + end: endInfo, + timeZone: context.timeZone, + localeCodes: context.locale.codes, + defaultSeparator: betterDefaultSeparator || context.defaultSeparator + }; + } + + /* + TODO: fix the terminology of "formatter" vs "formatting func" + */ + /* + At the time of instantiation, this object does not know which cmd-formatting system it will use. + It receives this at the time of formatting, as a setting. + */ + var CmdFormatter = /** @class */ (function () { + function CmdFormatter(cmdStr) { + this.cmdStr = cmdStr; + } + CmdFormatter.prototype.format = function (date, context, betterDefaultSeparator) { + return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, betterDefaultSeparator)); + }; + CmdFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { + return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, betterDefaultSeparator)); + }; + return CmdFormatter; + }()); + + var FuncFormatter = /** @class */ (function () { + function FuncFormatter(func) { + this.func = func; + } + FuncFormatter.prototype.format = function (date, context, betterDefaultSeparator) { + return this.func(createVerboseFormattingArg(date, null, context, betterDefaultSeparator)); + }; + FuncFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { + return this.func(createVerboseFormattingArg(start, end, context, betterDefaultSeparator)); + }; + return FuncFormatter; + }()); + + function createFormatter(input) { + if (typeof input === 'object' && input) { // non-null object + return new NativeFormatter(input); + } + else if (typeof input === 'string') { + return new CmdFormatter(input); + } + else if (typeof input === 'function') { + return new FuncFormatter(input); + } + } + + // base options + // ------------ + var BASE_OPTION_REFINERS = { + navLinkDayClick: identity, + navLinkWeekClick: identity, + duration: createDuration, + bootstrapFontAwesome: identity, + buttonIcons: identity, + customButtons: identity, + defaultAllDayEventDuration: createDuration, + defaultTimedEventDuration: createDuration, + nextDayThreshold: createDuration, + scrollTime: createDuration, + slotMinTime: createDuration, + slotMaxTime: createDuration, + dayPopoverFormat: createFormatter, + slotDuration: createDuration, + snapDuration: createDuration, + headerToolbar: identity, + footerToolbar: identity, + defaultRangeSeparator: String, + titleRangeSeparator: String, + forceEventDuration: Boolean, + dayHeaders: Boolean, + dayHeaderFormat: createFormatter, + dayHeaderClassNames: identity, + dayHeaderContent: identity, + dayHeaderDidMount: identity, + dayHeaderWillUnmount: identity, + dayCellClassNames: identity, + dayCellContent: identity, + dayCellDidMount: identity, + dayCellWillUnmount: identity, + initialView: String, + aspectRatio: Number, + weekends: Boolean, + weekNumberCalculation: identity, + weekNumbers: Boolean, + weekNumberClassNames: identity, + weekNumberContent: identity, + weekNumberDidMount: identity, + weekNumberWillUnmount: identity, + editable: Boolean, + viewClassNames: identity, + viewDidMount: identity, + viewWillUnmount: identity, + nowIndicator: Boolean, + nowIndicatorClassNames: identity, + nowIndicatorContent: identity, + nowIndicatorDidMount: identity, + nowIndicatorWillUnmount: identity, + showNonCurrentDates: Boolean, + lazyFetching: Boolean, + startParam: String, + endParam: String, + timeZoneParam: String, + timeZone: String, + locales: identity, + locale: identity, + themeSystem: String, + dragRevertDuration: Number, + dragScroll: Boolean, + allDayMaintainDuration: Boolean, + unselectAuto: Boolean, + dropAccept: identity, + eventOrder: parseFieldSpecs, + handleWindowResize: Boolean, + windowResizeDelay: Number, + longPressDelay: Number, + eventDragMinDistance: Number, + expandRows: Boolean, + height: identity, + contentHeight: identity, + direction: String, + weekNumberFormat: createFormatter, + eventResizableFromStart: Boolean, + displayEventTime: Boolean, + displayEventEnd: Boolean, + weekText: String, + progressiveEventRendering: Boolean, + businessHours: identity, + initialDate: identity, + now: identity, + eventDataTransform: identity, + stickyHeaderDates: identity, + stickyFooterScrollbar: identity, + viewHeight: identity, + defaultAllDay: Boolean, + eventSourceFailure: identity, + eventSourceSuccess: identity, + eventDisplay: String, + eventStartEditable: Boolean, + eventDurationEditable: Boolean, + eventOverlap: identity, + eventConstraint: identity, + eventAllow: identity, + eventBackgroundColor: String, + eventBorderColor: String, + eventTextColor: String, + eventColor: String, + eventClassNames: identity, + eventContent: identity, + eventDidMount: identity, + eventWillUnmount: identity, + selectConstraint: identity, + selectOverlap: identity, + selectAllow: identity, + droppable: Boolean, + unselectCancel: String, + slotLabelFormat: identity, + slotLaneClassNames: identity, + slotLaneContent: identity, + slotLaneDidMount: identity, + slotLaneWillUnmount: identity, + slotLabelClassNames: identity, + slotLabelContent: identity, + slotLabelDidMount: identity, + slotLabelWillUnmount: identity, + dayMaxEvents: identity, + dayMaxEventRows: identity, + dayMinWidth: Number, + slotLabelInterval: createDuration, + allDayText: String, + allDayClassNames: identity, + allDayContent: identity, + allDayDidMount: identity, + allDayWillUnmount: identity, + slotMinWidth: Number, + navLinks: Boolean, + eventTimeFormat: createFormatter, + rerenderDelay: Number, + moreLinkText: identity, + selectMinDistance: Number, + selectable: Boolean, + selectLongPressDelay: Number, + eventLongPressDelay: Number, + selectMirror: Boolean, + eventMinHeight: Number, + slotEventOverlap: Boolean, + plugins: identity, + firstDay: Number, + dayCount: Number, + dateAlignment: String, + dateIncrement: createDuration, + hiddenDays: identity, + monthMode: Boolean, + fixedWeekCount: Boolean, + validRange: identity, + visibleRange: identity, + titleFormat: identity, + // only used by list-view, but languages define the value, so we need it in base options + noEventsText: String + }; + // do NOT give a type here. need `typeof BASE_OPTION_DEFAULTS` to give real results. + // raw values. + var BASE_OPTION_DEFAULTS = { + eventDisplay: 'auto', + defaultRangeSeparator: ' - ', + titleRangeSeparator: ' \u2013 ', + defaultTimedEventDuration: '01:00:00', + defaultAllDayEventDuration: { day: 1 }, + forceEventDuration: false, + nextDayThreshold: '00:00:00', + dayHeaders: true, + initialView: '', + aspectRatio: 1.35, + headerToolbar: { + start: 'title', + center: '', + end: 'today prev,next' + }, + weekends: true, + weekNumbers: false, + weekNumberCalculation: 'local', + editable: false, + nowIndicator: false, + scrollTime: '06:00:00', + slotMinTime: '00:00:00', + slotMaxTime: '24:00:00', + showNonCurrentDates: true, + lazyFetching: true, + startParam: 'start', + endParam: 'end', + timeZoneParam: 'timeZone', + timeZone: 'local', + locales: [], + locale: '', + themeSystem: 'standard', + dragRevertDuration: 500, + dragScroll: true, + allDayMaintainDuration: false, + unselectAuto: true, + dropAccept: '*', + eventOrder: 'start,-duration,allDay,title', + dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' }, + handleWindowResize: true, + windowResizeDelay: 100, + longPressDelay: 1000, + eventDragMinDistance: 5, + expandRows: false, + navLinks: false, + selectable: false + }; + // calendar listeners + // ------------------ + var CALENDAR_LISTENER_REFINERS = { + datesSet: identity, + eventsSet: identity, + eventAdd: identity, + eventChange: identity, + eventRemove: identity, + windowResize: identity, + eventClick: identity, + eventMouseEnter: identity, + eventMouseLeave: identity, + select: identity, + unselect: identity, + loading: identity, + // internal + _unmount: identity, + _beforeprint: identity, + _afterprint: identity, + _noEventDrop: identity, + _noEventResize: identity, + _resize: identity, + _scrollRequest: identity + }; + // calendar-specific options + // ------------------------- + var CALENDAR_OPTION_REFINERS = { + buttonText: identity, + views: identity, + plugins: identity, + initialEvents: identity, + events: identity, + eventSources: identity + }; + var COMPLEX_OPTION_COMPARATORS = { + headerToolbar: isBoolComplexEqual, + footerToolbar: isBoolComplexEqual, + buttonText: isBoolComplexEqual, + buttonIcons: isBoolComplexEqual + }; + function isBoolComplexEqual(a, b) { + if (typeof a === 'object' && typeof b === 'object' && a && b) { // both non-null objects + return isPropsEqual(a, b); + } + else { + return a === b; + } + } + // view-specific options + // --------------------- + var VIEW_OPTION_REFINERS = { + type: String, + component: identity, + buttonText: String, + buttonTextKey: String, + dateProfileGeneratorClass: identity, + usesMinMaxTime: Boolean, + classNames: identity, + content: identity, + didMount: identity, + willUnmount: identity + }; + // util funcs + // ---------------------------------------------------------------------------------------------------- + function mergeRawOptions(optionSets) { + return mergeProps(optionSets, COMPLEX_OPTION_COMPARATORS); + } + function refineProps(input, refiners) { + var refined = {}; + var extra = {}; + for (var propName in refiners) { + if (propName in input) { + refined[propName] = refiners[propName](input[propName]); + } + } + for (var propName in input) { + if (!(propName in refiners)) { + extra[propName] = input[propName]; + } + } + return { refined: refined, extra: extra }; + } + function identity(raw) { + return raw; + } + + function parseEvents(rawEvents, eventSource, context, allowOpenRange) { + var eventStore = createEmptyEventStore(); + var eventRefiners = buildEventRefiners(context); + for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) { + var rawEvent = rawEvents_1[_i]; + var tuple = parseEvent(rawEvent, eventSource, context, allowOpenRange, eventRefiners); + if (tuple) { + eventTupleToStore(tuple, eventStore); + } + } + return eventStore; + } + function eventTupleToStore(tuple, eventStore) { + if (eventStore === void 0) { eventStore = createEmptyEventStore(); } + eventStore.defs[tuple.def.defId] = tuple.def; + if (tuple.instance) { + eventStore.instances[tuple.instance.instanceId] = tuple.instance; + } + return eventStore; + } + // retrieves events that have the same groupId as the instance specified by `instanceId` + // or they are the same as the instance. + // why might instanceId not be in the store? an event from another calendar? + function getRelevantEvents(eventStore, instanceId) { + var instance = eventStore.instances[instanceId]; + if (instance) { + var def_1 = eventStore.defs[instance.defId]; + // get events/instances with same group + var newStore = filterEventStoreDefs(eventStore, function (lookDef) { + return isEventDefsGrouped(def_1, lookDef); + }); + // add the original + // TODO: wish we could use eventTupleToStore or something like it + newStore.defs[def_1.defId] = def_1; + newStore.instances[instance.instanceId] = instance; + return newStore; + } + return createEmptyEventStore(); + } + function isEventDefsGrouped(def0, def1) { + return Boolean(def0.groupId && def0.groupId === def1.groupId); + } + function createEmptyEventStore() { + return { defs: {}, instances: {} }; + } + function mergeEventStores(store0, store1) { + return { + defs: __assign(__assign({}, store0.defs), store1.defs), + instances: __assign(__assign({}, store0.instances), store1.instances) + }; + } + function filterEventStoreDefs(eventStore, filterFunc) { + var defs = filterHash(eventStore.defs, filterFunc); + var instances = filterHash(eventStore.instances, function (instance) { + return defs[instance.defId]; // still exists? + }); + return { defs: defs, instances: instances }; + } + function excludeSubEventStore(master, sub) { + var defs = master.defs, instances = master.instances; + var filteredDefs = {}; + var filteredInstances = {}; + for (var defId in defs) { + if (!sub.defs[defId]) { // not explicitly excluded + filteredDefs[defId] = defs[defId]; + } + } + for (var instanceId in instances) { + if (!sub.instances[instanceId] && // not explicitly excluded + filteredDefs[instances[instanceId].defId] // def wasn't filtered away + ) { + filteredInstances[instanceId] = instances[instanceId]; + } + } + return { + defs: filteredDefs, + instances: filteredInstances + }; + } + + function normalizeConstraint(input, context) { + if (Array.isArray(input)) { + return parseEvents(input, null, context, true); // allowOpenRange=true + } + else if (typeof input === 'object' && input) { // non-null object + return parseEvents([input], null, context, true); // allowOpenRange=true + } + else if (input != null) { + return String(input); + } + else { + return null; + } + } + + function parseClassNames(raw) { + if (Array.isArray(raw)) { + return raw; + } + else if (typeof raw === 'string') { + return raw.split(/\s+/); + } + else { + return []; + } + } + + // TODO: better called "EventSettings" or "EventConfig" + // TODO: move this file into structs + // TODO: separate constraint/overlap/allow, because selection uses only that, not other props + var EVENT_UI_REFINERS = { + display: String, + editable: Boolean, + startEditable: Boolean, + durationEditable: Boolean, + constraint: identity, + overlap: identity, + allow: identity, + className: parseClassNames, + classNames: parseClassNames, + color: String, + backgroundColor: String, + borderColor: String, + textColor: String + }; + function createEventUi(refined, context) { + var constraint = normalizeConstraint(refined.constraint, context); + return { + display: refined.display || null, + startEditable: refined.startEditable != null ? refined.startEditable : refined.editable, + durationEditable: refined.durationEditable != null ? refined.durationEditable : refined.editable, + constraints: constraint != null ? [constraint] : [], + overlap: refined.overlap != null ? refined.overlap : null, + allows: refined.allow != null ? [refined.allow] : [], + backgroundColor: refined.backgroundColor || refined.color || '', + borderColor: refined.borderColor || refined.color || '', + textColor: refined.textColor || '', + classNames: (refined.className || []).concat(refined.classNames || []) // join singular and plural + }; + } + // TODO: prevent against problems with <2 args! + function combineEventUis(uis) { + return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI); + } + function combineTwoEventUis(item0, item1) { + return { + display: item1.display != null ? item1.display : item0.display, + startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable, + durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable, + constraints: item0.constraints.concat(item1.constraints), + overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap, + allows: item0.allows.concat(item1.allows), + backgroundColor: item1.backgroundColor || item0.backgroundColor, + borderColor: item1.borderColor || item0.borderColor, + textColor: item1.textColor || item0.textColor, + classNames: item0.classNames.concat(item1.classNames) + }; + } + var EMPTY_EVENT_UI = { + display: null, + startEditable: null, + durationEditable: null, + constraints: [], + overlap: null, + allows: [], + backgroundColor: '', + borderColor: '', + textColor: '', + classNames: [] + }; + + var EVENT_NON_DATE_REFINERS = { + id: String, + groupId: String, + title: String, + url: String + }; + var EVENT_DATE_REFINERS = { + start: identity, + end: identity, + date: identity, + allDay: Boolean + }; + var EVENT_REFINERS = __assign(__assign(__assign({}, EVENT_NON_DATE_REFINERS), EVENT_DATE_REFINERS), { extendedProps: identity }); + function parseEvent(raw, eventSource, context, allowOpenRange, refiners) { + if (refiners === void 0) { refiners = buildEventRefiners(context); } + var _a = refineEventDef(raw, context, refiners), refined = _a.refined, extra = _a.extra; + var defaultAllDay = computeIsDefaultAllDay(eventSource, context); + var recurringRes = parseRecurring(refined, defaultAllDay, context.dateEnv, context.pluginHooks.recurringTypes); + if (recurringRes) { + var def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', recurringRes.allDay, Boolean(recurringRes.duration), context); + def.recurringDef = { + typeId: recurringRes.typeId, + typeData: recurringRes.typeData, + duration: recurringRes.duration + }; + return { def: def, instance: null }; + } + else { + var singleRes = parseSingle(refined, defaultAllDay, context, allowOpenRange); + if (singleRes) { + var def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', singleRes.allDay, singleRes.hasEnd, context); + var instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo); + return { def: def, instance: instance }; + } + } + return null; + } + function refineEventDef(raw, context, refiners) { + if (refiners === void 0) { refiners = buildEventRefiners(context); } + return refineProps(raw, refiners); + } + function buildEventRefiners(context) { + return __assign(__assign(__assign({}, EVENT_UI_REFINERS), EVENT_REFINERS), context.pluginHooks.eventRefiners); + } + /* + Will NOT populate extendedProps with the leftover properties. + Will NOT populate date-related props. + */ + function parseEventDef(refined, extra, sourceId, allDay, hasEnd, context) { + var def = { + title: refined.title || '', + groupId: refined.groupId || '', + publicId: refined.id || '', + url: refined.url || '', + recurringDef: null, + defId: guid(), + sourceId: sourceId, + allDay: allDay, + hasEnd: hasEnd, + ui: createEventUi(refined, context), + extendedProps: __assign(__assign({}, (refined.extendedProps || {})), extra) + }; + for (var _i = 0, _a = context.pluginHooks.eventDefMemberAdders; _i < _a.length; _i++) { + var memberAdder = _a[_i]; + __assign(def, memberAdder(refined)); + } + // help out EventApi from having user modify props + Object.freeze(def.ui.classNames); + Object.freeze(def.extendedProps); + return def; + } + function parseSingle(refined, defaultAllDay, context, allowOpenRange) { + var allDay = refined.allDay; + var startMeta; + var startMarker = null; + var hasEnd = false; + var endMeta; + var endMarker = null; + var startInput = refined.start != null ? refined.start : refined.date; + startMeta = context.dateEnv.createMarkerMeta(startInput); + if (startMeta) { + startMarker = startMeta.marker; + } + else if (!allowOpenRange) { + return null; + } + if (refined.end != null) { + endMeta = context.dateEnv.createMarkerMeta(refined.end); + } + if (allDay == null) { + if (defaultAllDay != null) { + allDay = defaultAllDay; + } + else { + // fall back to the date props LAST + allDay = (!startMeta || startMeta.isTimeUnspecified) && + (!endMeta || endMeta.isTimeUnspecified); + } + } + if (allDay && startMarker) { + startMarker = startOfDay(startMarker); + } + if (endMeta) { + endMarker = endMeta.marker; + if (allDay) { + endMarker = startOfDay(endMarker); + } + if (startMarker && endMarker <= startMarker) { + endMarker = null; + } + } + if (endMarker) { + hasEnd = true; + } + else if (!allowOpenRange) { + hasEnd = context.options.forceEventDuration || false; + endMarker = context.dateEnv.add(startMarker, allDay ? + context.options.defaultAllDayEventDuration : + context.options.defaultTimedEventDuration); + } + return { + allDay: allDay, + hasEnd: hasEnd, + range: { start: startMarker, end: endMarker }, + forcedStartTzo: startMeta ? startMeta.forcedTzo : null, + forcedEndTzo: endMeta ? endMeta.forcedTzo : null + }; + } + function computeIsDefaultAllDay(eventSource, context) { + var res = null; + if (eventSource) { + res = eventSource.defaultAllDay; + } + if (res == null) { + res = context.options.defaultAllDay; + } + return res; + } + + /* Date stuff that doesn't belong in datelib core + ----------------------------------------------------------------------------------------------------------------------*/ + // given a timed range, computes an all-day range that has the same exact duration, + // but whose start time is aligned with the start of the day. + function computeAlignedDayRange(timedRange) { + var dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1; + var start = startOfDay(timedRange.start); + var end = addDays(start, dayCnt); + return { start: start, end: end }; + } + // given a timed range, computes an all-day range based on how for the end date bleeds into the next day + // TODO: give nextDayThreshold a default arg + function computeVisibleDayRange(timedRange, nextDayThreshold) { + if (nextDayThreshold === void 0) { nextDayThreshold = createDuration(0); } + var startDay = null; + var endDay = null; + if (timedRange.end) { + endDay = startOfDay(timedRange.end); + var endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay` + // If the end time is actually inclusively part of the next day and is equal to or + // beyond the next day threshold, adjust the end to be the exclusive end of `endDay`. + // Otherwise, leaving it as inclusive will cause it to exclude `endDay`. + if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) { + endDay = addDays(endDay, 1); + } + } + if (timedRange.start) { + startDay = startOfDay(timedRange.start); // the beginning of the day the range starts + // If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day. + if (endDay && endDay <= startDay) { + endDay = addDays(startDay, 1); + } + } + return { start: startDay, end: endDay }; + } + // spans from one day into another? + function isMultiDayRange(range) { + var visibleRange = computeVisibleDayRange(range); + return diffDays(visibleRange.start, visibleRange.end) > 1; + } + function diffDates(date0, date1, dateEnv, largeUnit) { + if (largeUnit === 'year') { + return createDuration(dateEnv.diffWholeYears(date0, date1), 'year'); + } + else if (largeUnit === 'month') { + return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month'); + } + else { + return diffDayAndTime(date0, date1); // returns a duration + } + } + + function parseRange(input, dateEnv) { + var start = null; + var end = null; + if (input.start) { + start = dateEnv.createMarker(input.start); + } + if (input.end) { + end = dateEnv.createMarker(input.end); + } + if (!start && !end) { + return null; + } + if (start && end && end < start) { + return null; + } + return { start: start, end: end }; + } + // SIDE-EFFECT: will mutate ranges. + // Will return a new array result. + function invertRanges(ranges, constraintRange) { + var invertedRanges = []; + var start = constraintRange.start; // the end of the previous range. the start of the new range + var i; + var dateRange; + // ranges need to be in order. required for our date-walking algorithm + ranges.sort(compareRanges); + for (i = 0; i < ranges.length; i++) { + dateRange = ranges[i]; + // add the span of time before the event (if there is any) + if (dateRange.start > start) { // compare millisecond time (skip any ambig logic) + invertedRanges.push({ start: start, end: dateRange.start }); + } + if (dateRange.end > start) { + start = dateRange.end; + } + } + // add the span of time after the last event (if there is any) + if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic) + invertedRanges.push({ start: start, end: constraintRange.end }); + } + return invertedRanges; + } + function compareRanges(range0, range1) { + return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first + } + function intersectRanges(range0, range1) { + var start = range0.start; + var end = range0.end; + var newRange = null; + if (range1.start !== null) { + if (start === null) { + start = range1.start; + } + else { + start = new Date(Math.max(start.valueOf(), range1.start.valueOf())); + } + } + if (range1.end != null) { + if (end === null) { + end = range1.end; + } + else { + end = new Date(Math.min(end.valueOf(), range1.end.valueOf())); + } + } + if (start === null || end === null || start < end) { + newRange = { start: start, end: end }; + } + return newRange; + } + function rangesEqual(range0, range1) { + return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) && + (range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf()); + } + function rangesIntersect(range0, range1) { + return (range0.end === null || range1.start === null || range0.end > range1.start) && + (range0.start === null || range1.end === null || range0.start < range1.end); + } + function rangeContainsRange(outerRange, innerRange) { + return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) && + (outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end)); + } + function rangeContainsMarker(range, date) { + return (range.start === null || date >= range.start) && + (range.end === null || date < range.end); + } + // If the given date is not within the given range, move it inside. + // (If it's past the end, make it one millisecond before the end). + function constrainMarkerToRange(date, range) { + if (range.start != null && date < range.start) { + return range.start; + } + if (range.end != null && date >= range.end) { + return new Date(range.end.valueOf() - 1); + } + return date; + } + + /* + Specifying nextDayThreshold signals that all-day ranges should be sliced. + */ + function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) { + var inverseBgByGroupId = {}; + var inverseBgByDefId = {}; + var defByGroupId = {}; + var bgRanges = []; + var fgRanges = []; + var eventUis = compileEventUis(eventStore.defs, eventUiBases); + for (var defId in eventStore.defs) { + var def = eventStore.defs[defId]; + var ui = eventUis[def.defId]; + if (ui.display === 'inverse-background') { + if (def.groupId) { + inverseBgByGroupId[def.groupId] = []; + if (!defByGroupId[def.groupId]) { + defByGroupId[def.groupId] = def; + } + } + else { + inverseBgByDefId[defId] = []; + } + } + } + for (var instanceId in eventStore.instances) { + var instance = eventStore.instances[instanceId]; + var def = eventStore.defs[instance.defId]; + var ui = eventUis[def.defId]; + var origRange = instance.range; + var normalRange = (!def.allDay && nextDayThreshold) ? + computeVisibleDayRange(origRange, nextDayThreshold) : + origRange; + var slicedRange = intersectRanges(normalRange, framingRange); + if (slicedRange) { + if (ui.display === 'inverse-background') { + if (def.groupId) { + inverseBgByGroupId[def.groupId].push(slicedRange); + } + else { + inverseBgByDefId[instance.defId].push(slicedRange); + } + } + else if (ui.display !== 'none') { + (ui.display === 'background' ? bgRanges : fgRanges).push({ + def: def, + ui: ui, + instance: instance, + range: slicedRange, + isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(), + isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf() + }); + } + } + } + for (var groupId in inverseBgByGroupId) { // BY GROUP + var ranges = inverseBgByGroupId[groupId]; + var invertedRanges = invertRanges(ranges, framingRange); + for (var _i = 0, invertedRanges_1 = invertedRanges; _i < invertedRanges_1.length; _i++) { + var invertedRange = invertedRanges_1[_i]; + var def = defByGroupId[groupId]; + var ui = eventUis[def.defId]; + bgRanges.push({ + def: def, + ui: ui, + instance: null, + range: invertedRange, + isStart: false, + isEnd: false + }); + } + } + for (var defId in inverseBgByDefId) { + var ranges = inverseBgByDefId[defId]; + var invertedRanges = invertRanges(ranges, framingRange); + for (var _a = 0, invertedRanges_2 = invertedRanges; _a < invertedRanges_2.length; _a++) { + var invertedRange = invertedRanges_2[_a]; + bgRanges.push({ + def: eventStore.defs[defId], + ui: eventUis[defId], + instance: null, + range: invertedRange, + isStart: false, + isEnd: false + }); + } + } + return { bg: bgRanges, fg: fgRanges }; + } + function hasBgRendering(def) { + return def.ui.display === 'background' || def.ui.display === 'inverse-background'; + } + function setElSeg(el, seg) { + el.fcSeg = seg; + } + function getElSeg(el) { + return el.fcSeg || + el.parentNode.fcSeg || // for the harness + null; + } + // event ui computation + function compileEventUis(eventDefs, eventUiBases) { + return mapHash(eventDefs, function (eventDef) { + return compileEventUi(eventDef, eventUiBases); + }); + } + function compileEventUi(eventDef, eventUiBases) { + var uis = []; + if (eventUiBases['']) { + uis.push(eventUiBases['']); + } + if (eventUiBases[eventDef.defId]) { + uis.push(eventUiBases[eventDef.defId]); + } + uis.push(eventDef.ui); + return combineEventUis(uis); + } + function sortEventSegs(segs, eventOrderSpecs) { + var objs = segs.map(buildSegCompareObj); + objs.sort(function (obj0, obj1) { + return compareByFieldSpecs(obj0, obj1, eventOrderSpecs); + }); + return objs.map(function (c) { + return c._seg; + }); + } + // returns a object with all primitive props that can be compared + function buildSegCompareObj(seg) { + var eventRange = seg.eventRange; + var eventDef = eventRange.def; + var range = eventRange.instance ? eventRange.instance.range : eventRange.range; + var start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events + var end = range.end ? range.end.valueOf() : 0; // " + return __assign(__assign(__assign({}, eventDef.extendedProps), eventDef), { id: eventDef.publicId, start: start, + end: end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg // for later retrieval + }); + } + function computeSegDraggable(seg, context) { + var pluginHooks = context.pluginHooks; + var transformers = pluginHooks.isDraggableTransformers; + var _a = seg.eventRange, def = _a.def, ui = _a.ui; + var val = ui.startEditable; + for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { + var transformer = transformers_1[_i]; + val = transformer(val, def, ui, context); + } + return val; + } + function computeSegStartResizable(seg, context) { + return seg.isStart && seg.eventRange.ui.durationEditable && context.options.eventResizableFromStart; + } + function computeSegEndResizable(seg, context) { + return seg.isEnd && seg.eventRange.ui.durationEditable; + } + function buildSegTimeText(seg, timeFormat, context, defaultDisplayEventTime, // defaults to true + defaultDisplayEventEnd, // defaults to true + startOverride, endOverride) { + var dateEnv = context.dateEnv, options = context.options; + var displayEventTime = options.displayEventTime, displayEventEnd = options.displayEventEnd; + var eventDef = seg.eventRange.def; + var eventInstance = seg.eventRange.instance; + if (displayEventTime == null) { + displayEventTime = defaultDisplayEventTime !== false; + } + if (displayEventEnd == null) { + displayEventEnd = defaultDisplayEventEnd !== false; + } + if (displayEventTime && !eventDef.allDay && (seg.isStart || seg.isEnd)) { + var segStart = startOverride || (seg.isStart ? eventInstance.range.start : (seg.start || seg.eventRange.range.start)); + var segEnd = endOverride || (seg.isEnd ? eventInstance.range.end : (seg.end || seg.eventRange.range.end)); + if (displayEventEnd && eventDef.hasEnd) { + return dateEnv.formatRange(segStart, segEnd, timeFormat, { + forcedStartTzo: startOverride ? null : eventInstance.forcedStartTzo, + forcedEndTzo: endOverride ? null : eventInstance.forcedEndTzo + }); + } + else { + return dateEnv.format(segStart, timeFormat, { + forcedTzo: startOverride ? null : eventInstance.forcedStartTzo // nooooo, same + }); + } + } + return ''; + } + function getSegMeta(seg, todayRange, nowDate) { + var segRange = seg.eventRange.range; + return { + isPast: segRange.end < (nowDate || todayRange.start), + isFuture: segRange.start >= (nowDate || todayRange.end), + isToday: todayRange && rangeContainsMarker(todayRange, segRange.start) + }; + } + function getEventClassNames(props) { + var classNames = ['fc-event']; + if (props.isMirror) { + classNames.push('fc-event-mirror'); + } + if (props.isDraggable) { + classNames.push('fc-event-draggable'); + } + if (props.isStartResizable || props.isEndResizable) { + classNames.push('fc-event-resizable'); + } + if (props.isDragging) { + classNames.push('fc-event-dragging'); + } + if (props.isResizing) { + classNames.push('fc-event-resizing'); + } + if (props.isSelected) { + classNames.push('fc-event-selected'); + } + if (props.isStart) { + classNames.push('fc-event-start'); + } + if (props.isEnd) { + classNames.push('fc-event-end'); + } + if (props.isPast) { + classNames.push('fc-event-past'); + } + if (props.isToday) { + classNames.push('fc-event-today'); + } + if (props.isFuture) { + classNames.push('fc-event-future'); + } + return classNames; + } + function buildEventRangeKey(eventRange) { + return eventRange.instance + ? eventRange.instance.instanceId + : eventRange.def.defId + ':' + eventRange.range.start.toISOString(); + // inverse-background events don't have specific instances. TODO: better solution + } + + var STANDARD_PROPS = { + start: identity, + end: identity, + allDay: Boolean + }; + function parseDateSpan(raw, dateEnv, defaultDuration) { + var span = parseOpenDateSpan(raw, dateEnv); + var range = span.range; + if (!range.start) { + return null; + } + if (!range.end) { + if (defaultDuration == null) { + return null; + } + else { + range.end = dateEnv.add(range.start, defaultDuration); + } + } + return span; + } + /* + TODO: somehow combine with parseRange? + Will return null if the start/end props were present but parsed invalidly. + */ + function parseOpenDateSpan(raw, dateEnv) { + var _a = refineProps(raw, STANDARD_PROPS), standardProps = _a.refined, extra = _a.extra; + var startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null; + var endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null; + var allDay = standardProps.allDay; + if (allDay == null) { + allDay = (startMeta && startMeta.isTimeUnspecified) && + (!endMeta || endMeta.isTimeUnspecified); + } + return __assign({ range: { + start: startMeta ? startMeta.marker : null, + end: endMeta ? endMeta.marker : null, + }, allDay: allDay }, extra); + } + function isDateSpansEqual(span0, span1) { + return rangesEqual(span0.range, span1.range) && + span0.allDay === span1.allDay && + isSpanPropsEqual(span0, span1); + } + // the NON-DATE-RELATED props + function isSpanPropsEqual(span0, span1) { + for (var propName in span1) { + if (propName !== 'range' && propName !== 'allDay') { + if (span0[propName] !== span1[propName]) { + return false; + } + } + } + // are there any props that span0 has that span1 DOESN'T have? + // both have range/allDay, so no need to special-case. + for (var propName in span0) { + if (!(propName in span1)) { + return false; + } + } + return true; + } + function buildDateSpanApi(span, dateEnv) { + return __assign(__assign({}, buildRangeApi(span.range, dateEnv, span.allDay)), { allDay: span.allDay }); + } + function buildRangeApiWithTimeZone(range, dateEnv, omitTime) { + return __assign(__assign({}, buildRangeApi(range, dateEnv, omitTime)), { timeZone: dateEnv.timeZone }); + } + function buildRangeApi(range, dateEnv, omitTime) { + return { + start: dateEnv.toDate(range.start), + end: dateEnv.toDate(range.end), + startStr: dateEnv.formatIso(range.start, { omitTime: omitTime }), + endStr: dateEnv.formatIso(range.end, { omitTime: omitTime }) + }; + } + function fabricateEventRange(dateSpan, eventUiBases, context) { + var res = refineEventDef({ editable: false }, context); + var def = parseEventDef(res.refined, res.extra, '', // sourceId + dateSpan.allDay, true, // hasEnd + context); + return { + def: def, + ui: compileEventUi(def, eventUiBases), + instance: createEventInstance(def.defId, dateSpan.range), + range: dateSpan.range, + isStart: true, + isEnd: true + }; + } + + function triggerDateSelect(selection, pev, context) { + context.emitter.trigger('select', __assign(__assign({}, buildDateSpanApiWithContext(selection, context)), { jsEvent: pev ? pev.origEvent : null, view: context.viewApi || context.calendarApi.view })); + } + function triggerDateUnselect(pev, context) { + context.emitter.trigger('unselect', { + jsEvent: pev ? pev.origEvent : null, + view: context.viewApi || context.calendarApi.view + }); + } + function buildDateSpanApiWithContext(dateSpan, context) { + var props = {}; + for (var _i = 0, _a = context.pluginHooks.dateSpanTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(props, transform(dateSpan, context)); + } + __assign(props, buildDateSpanApi(dateSpan, context.dateEnv)); + return props; + } + // Given an event's allDay status and start date, return what its fallback end date should be. + // TODO: rename to computeDefaultEventEnd + function getDefaultEventEnd(allDay, marker, context) { + var dateEnv = context.dateEnv, options = context.options; + var end = marker; + if (allDay) { + end = startOfDay(end); + end = dateEnv.add(end, options.defaultAllDayEventDuration); + } + else { + end = dateEnv.add(end, options.defaultTimedEventDuration); + } + return end; + } + + // applies the mutation to ALL defs/instances within the event store + function applyMutationToEventStore(eventStore, eventConfigBase, mutation, context) { + var eventConfigs = compileEventUis(eventStore.defs, eventConfigBase); + var dest = createEmptyEventStore(); + for (var defId in eventStore.defs) { + var def = eventStore.defs[defId]; + dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, context); + } + for (var instanceId in eventStore.instances) { + var instance = eventStore.instances[instanceId]; + var def = dest.defs[instance.defId]; // important to grab the newly modified def + dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, context); + } + return dest; + } + function applyMutationToEventDef(eventDef, eventConfig, mutation, context) { + var standardProps = mutation.standardProps || {}; + // if hasEnd has not been specified, guess a good value based on deltas. + // if duration will change, there's no way the default duration will persist, + // and thus, we need to mark the event as having a real end + if (standardProps.hasEnd == null && + eventConfig.durationEditable && + (mutation.startDelta || mutation.endDelta)) { + standardProps.hasEnd = true; // TODO: is this mutation okay? + } + var copy = __assign(__assign(__assign({}, eventDef), standardProps), { ui: __assign(__assign({}, eventDef.ui), standardProps.ui) }); + if (mutation.extendedProps) { + copy.extendedProps = __assign(__assign({}, copy.extendedProps), mutation.extendedProps); + } + for (var _i = 0, _a = context.pluginHooks.eventDefMutationAppliers; _i < _a.length; _i++) { + var applier = _a[_i]; + applier(copy, mutation, context); + } + if (!copy.hasEnd && context.options.forceEventDuration) { + copy.hasEnd = true; + } + return copy; + } + function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef + eventConfig, mutation, context) { + var dateEnv = context.dateEnv; + var forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true; + var clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false; + var copy = __assign({}, eventInstance); + if (forceAllDay) { + copy.range = computeAlignedDayRange(copy.range); + } + if (mutation.datesDelta && eventConfig.startEditable) { + copy.range = { + start: dateEnv.add(copy.range.start, mutation.datesDelta), + end: dateEnv.add(copy.range.end, mutation.datesDelta) + }; + } + if (mutation.startDelta && eventConfig.durationEditable) { + copy.range = { + start: dateEnv.add(copy.range.start, mutation.startDelta), + end: copy.range.end + }; + } + if (mutation.endDelta && eventConfig.durationEditable) { + copy.range = { + start: copy.range.start, + end: dateEnv.add(copy.range.end, mutation.endDelta) + }; + } + if (clearEnd) { + copy.range = { + start: copy.range.start, + end: getDefaultEventEnd(eventDef.allDay, copy.range.start, context) + }; + } + // in case event was all-day but the supplied deltas were not + // better util for this? + if (eventDef.allDay) { + copy.range = { + start: startOfDay(copy.range.start), + end: startOfDay(copy.range.end) + }; + } + // handle invalid durations + if (copy.range.end < copy.range.start) { + copy.range.end = getDefaultEventEnd(eventDef.allDay, copy.range.start, context); + } + return copy; + } + + // no public types yet. when there are, export from: + // import {} from './api-type-deps' + var ViewApi = /** @class */ (function () { + function ViewApi(type, getCurrentData, dateEnv) { + this.type = type; + this.getCurrentData = getCurrentData; + this.dateEnv = dateEnv; + } + Object.defineProperty(ViewApi.prototype, "calendar", { + get: function () { + return this.getCurrentData().calendarApi; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ViewApi.prototype, "title", { + get: function () { + return this.getCurrentData().viewTitle; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ViewApi.prototype, "activeStart", { + get: function () { + return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.start); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ViewApi.prototype, "activeEnd", { + get: function () { + return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.end); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ViewApi.prototype, "currentStart", { + get: function () { + return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.start); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ViewApi.prototype, "currentEnd", { + get: function () { + return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.end); + }, + enumerable: false, + configurable: true + }); + ViewApi.prototype.getOption = function (name) { + return this.getCurrentData().options[name]; // are the view-specific options + }; + return ViewApi; + }()); + + var EVENT_SOURCE_REFINERS = { + id: String, + defaultAllDay: Boolean, + url: String, + events: identity, + eventDataTransform: identity, + // for any network-related sources + success: identity, + failure: identity, + }; + function parseEventSource(raw, context, refiners) { + if (refiners === void 0) { refiners = buildEventSourceRefiners(context); } + var rawObj; + if (typeof raw === 'string') { + rawObj = { url: raw }; + } + else if (typeof raw === 'function' || Array.isArray(raw)) { + rawObj = { events: raw }; + } + else if (typeof raw === 'object' && raw) { // not null + rawObj = raw; + } + if (rawObj) { + var _a = refineProps(rawObj, refiners), refined = _a.refined, extra = _a.extra; + var metaRes = buildEventSourceMeta(refined, context); + if (metaRes) { + return { + _raw: raw, + isFetching: false, + latestFetchId: '', + fetchRange: null, + defaultAllDay: refined.defaultAllDay, + eventDataTransform: refined.eventDataTransform, + success: refined.success, + failure: refined.failure, + publicId: refined.id || '', + sourceId: guid(), + sourceDefId: metaRes.sourceDefId, + meta: metaRes.meta, + ui: createEventUi(refined, context), + extendedProps: extra + }; + } + } + return null; + } + function buildEventSourceRefiners(context) { + return __assign(__assign(__assign({}, EVENT_UI_REFINERS), EVENT_SOURCE_REFINERS), context.pluginHooks.eventSourceRefiners); + } + function buildEventSourceMeta(raw, context) { + var defs = context.pluginHooks.eventSourceDefs; + for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence + var def = defs[i]; + var meta = def.parseMeta(raw); + if (meta) { + return { sourceDefId: i, meta: meta }; + } + } + return null; + } + + function reduceCurrentDate(currentDate, action) { + switch (action.type) { + case 'CHANGE_DATE': + return action.dateMarker; + default: + return currentDate; + } + } + function getInitialDate(options, dateEnv) { + var initialDateInput = options.initialDate; + // compute the initial ambig-timezone date + if (initialDateInput != null) { + return dateEnv.createMarker(initialDateInput); + } + else { + return getNow(options.now, dateEnv); // getNow already returns unzoned + } + } + function getNow(nowInput, dateEnv) { + if (typeof nowInput === 'function') { + nowInput = nowInput(); + } + if (nowInput == null) { + return dateEnv.createNowMarker(); + } + return dateEnv.createMarker(nowInput); + } + + var CalendarApi = /** @class */ (function () { + function CalendarApi() { + } + CalendarApi.prototype.getCurrentData = function () { + return this.currentDataManager.getCurrentData(); + }; + CalendarApi.prototype.dispatch = function (action) { + return this.currentDataManager.dispatch(action); + }; + Object.defineProperty(CalendarApi.prototype, "view", { + get: function () { return this.getCurrentData().viewApi; } // for public API + , + enumerable: false, + configurable: true + }); + CalendarApi.prototype.batchRendering = function (callback) { + callback(); + }; + CalendarApi.prototype.updateSize = function () { + this.trigger('_resize', true); + }; + // Options + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.setOption = function (name, val) { + this.dispatch({ + type: 'SET_OPTION', + optionName: name, + rawOptionValue: val + }); + }; + CalendarApi.prototype.getOption = function (name) { + return this.currentDataManager.currentCalendarOptionsInput[name]; + }; + CalendarApi.prototype.getAvailableLocaleCodes = function () { + return Object.keys(this.getCurrentData().availableRawLocales); + }; + // Trigger + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.on = function (handlerName, handler) { + var currentDataManager = this.currentDataManager; + if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) { + currentDataManager.emitter.on(handlerName, handler); + } + else { + console.warn("Unknown listener name '" + handlerName + "'"); + } + }; + CalendarApi.prototype.off = function (handlerName, handler) { + this.currentDataManager.emitter.off(handlerName, handler); + }; + // not meant for public use + CalendarApi.prototype.trigger = function (handlerName) { + var _a; + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + (_a = this.currentDataManager.emitter).trigger.apply(_a, __spreadArrays([handlerName], args)); + }; + // View + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.changeView = function (viewType, dateOrRange) { + var _this = this; + this.batchRendering(function () { + _this.unselect(); + if (dateOrRange) { + if (dateOrRange.start && dateOrRange.end) { // a range + _this.dispatch({ + type: 'CHANGE_VIEW_TYPE', + viewType: viewType, + }); + _this.dispatch({ + type: 'SET_OPTION', + optionName: 'visibleRange', + rawOptionValue: dateOrRange + }); + } + else { + var dateEnv = _this.getCurrentData().dateEnv; + _this.dispatch({ + type: 'CHANGE_VIEW_TYPE', + viewType: viewType, + dateMarker: dateEnv.createMarker(dateOrRange) + }); + } + } + else { + _this.dispatch({ + type: 'CHANGE_VIEW_TYPE', + viewType: viewType + }); + } + }); + }; + // Forces navigation to a view for the given date. + // `viewType` can be a specific view name or a generic one like "week" or "day". + // needs to change + CalendarApi.prototype.zoomTo = function (dateMarker, viewType) { + var state = this.getCurrentData(); + var spec; + viewType = viewType || 'day'; // day is default zoom + spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType); + this.unselect(); + if (spec) { + this.dispatch({ + type: 'CHANGE_VIEW_TYPE', + viewType: spec.type, + dateMarker: dateMarker + }); + } + else { + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: dateMarker + }); + } + }; + // Given a duration singular unit, like "week" or "day", finds a matching view spec. + // Preference is given to views that have corresponding buttons. + CalendarApi.prototype.getUnitViewSpec = function (unit) { + var _a = this.getCurrentData(), viewSpecs = _a.viewSpecs, toolbarConfig = _a.toolbarConfig; + var viewTypes = [].concat(toolbarConfig.viewsWithButtons); + var i; + var spec; + for (var viewType in viewSpecs) { + viewTypes.push(viewType); + } + for (i = 0; i < viewTypes.length; i++) { + spec = viewSpecs[viewTypes[i]]; + if (spec) { + if (spec.singleUnit === unit) { + return spec; + } + } + } + }; + // Current Date + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.prev = function () { + this.unselect(); + this.dispatch({ type: 'PREV' }); + }; + CalendarApi.prototype.next = function () { + this.unselect(); + this.dispatch({ type: 'NEXT' }); + }; + CalendarApi.prototype.prevYear = function () { + var state = this.getCurrentData(); + this.unselect(); + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: state.dateEnv.addYears(state.currentDate, -1) + }); + }; + CalendarApi.prototype.nextYear = function () { + var state = this.getCurrentData(); + this.unselect(); + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: state.dateEnv.addYears(state.currentDate, 1) + }); + }; + CalendarApi.prototype.today = function () { + var state = this.getCurrentData(); + this.unselect(); + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: getNow(state.calendarOptions.now, state.dateEnv) + }); + }; + CalendarApi.prototype.gotoDate = function (zonedDateInput) { + var state = this.getCurrentData(); + this.unselect(); + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: state.dateEnv.createMarker(zonedDateInput) + }); + }; + CalendarApi.prototype.incrementDate = function (deltaInput) { + var state = this.getCurrentData(); + var delta = createDuration(deltaInput); + if (delta) { // else, warn about invalid input? + this.unselect(); + this.dispatch({ + type: 'CHANGE_DATE', + dateMarker: state.dateEnv.add(state.currentDate, delta) + }); + } + }; + // for external API + CalendarApi.prototype.getDate = function () { + var state = this.getCurrentData(); + return state.dateEnv.toDate(state.currentDate); + }; + // Date Formatting Utils + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.formatDate = function (d, formatter) { + var dateEnv = this.getCurrentData().dateEnv; + return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter)); + }; + // `settings` is for formatter AND isEndExclusive + CalendarApi.prototype.formatRange = function (d0, d1, settings) { + var dateEnv = this.getCurrentData().dateEnv; + return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings); + }; + CalendarApi.prototype.formatIso = function (d, omitTime) { + var dateEnv = this.getCurrentData().dateEnv; + return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime: omitTime }); + }; + // Date Selection / Event Selection / DayClick + // ----------------------------------------------------------------------------------------------------------------- + // this public method receives start/end dates in any format, with any timezone + // NOTE: args were changed from v3 + CalendarApi.prototype.select = function (dateOrObj, endDate) { + var selectionInput; + if (endDate == null) { + if (dateOrObj.start != null) { + selectionInput = dateOrObj; + } + else { + selectionInput = { + start: dateOrObj, + end: null + }; + } + } + else { + selectionInput = { + start: dateOrObj, + end: endDate + }; + } + var state = this.getCurrentData(); + var selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }) // TODO: cache this? + ); + if (selection) { // throw parse error otherwise? + this.dispatch({ type: 'SELECT_DATES', selection: selection }); + triggerDateSelect(selection, null, state); + } + }; + // public method + CalendarApi.prototype.unselect = function (pev) { + var state = this.getCurrentData(); + if (state.dateSelection) { + this.dispatch({ type: 'UNSELECT_DATES' }); + triggerDateUnselect(pev, state); + } + }; + // Public Events API + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.addEvent = function (eventInput, sourceInput) { + if (eventInput instanceof EventApi) { + var def = eventInput._def; + var instance = eventInput._instance; + var currentData = this.getCurrentData(); + // not already present? don't want to add an old snapshot + if (!currentData.eventStore.defs[def.defId]) { + this.dispatch({ + type: 'ADD_EVENTS', + eventStore: eventTupleToStore({ def: def, instance: instance }) // TODO: better util for two args? + }); + this.triggerEventAdd(eventInput); + } + return eventInput; + } + var state = this.getCurrentData(); + var eventSource; + if (sourceInput instanceof EventSourceApi) { + eventSource = sourceInput.internalEventSource; + } + else if (typeof sourceInput === 'boolean') { + if (sourceInput) { // true. part of the first event source + eventSource = hashValuesToArray(state.eventSources)[0]; + } + } + else if (sourceInput != null) { // an ID. accepts a number too + var sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function + if (!sourceApi) { + console.warn('Could not find an event source with ID "' + sourceInput + '"'); // TODO: test + return null; + } + else { + eventSource = sourceApi.internalEventSource; + } + } + var tuple = parseEvent(eventInput, eventSource, state, false); + if (tuple) { + var newEventApi = new EventApi(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance); + this.dispatch({ + type: 'ADD_EVENTS', + eventStore: eventTupleToStore(tuple) + }); + this.triggerEventAdd(newEventApi); + return newEventApi; + } + return null; + }; + CalendarApi.prototype.triggerEventAdd = function (eventApi) { + var _this = this; + var emitter = this.getCurrentData().emitter; + emitter.trigger('eventAdd', { + event: eventApi, + relatedEvents: [], + revert: function () { + _this.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: eventApiToStore(eventApi) + }); + } + }); + }; + // TODO: optimize + CalendarApi.prototype.getEventById = function (id) { + var state = this.getCurrentData(); + var _a = state.eventStore, defs = _a.defs, instances = _a.instances; + id = String(id); + for (var defId in defs) { + var def = defs[defId]; + if (def.publicId === id) { + if (def.recurringDef) { + return new EventApi(state, def, null); + } + else { + for (var instanceId in instances) { + var instance = instances[instanceId]; + if (instance.defId === def.defId) { + return new EventApi(state, def, instance); + } + } + } + } + } + return null; + }; + CalendarApi.prototype.getEvents = function () { + var currentData = this.getCurrentData(); + return buildEventApis(currentData.eventStore, currentData); + }; + CalendarApi.prototype.removeAllEvents = function () { + this.dispatch({ type: 'REMOVE_ALL_EVENTS' }); + }; + // Public Event Sources API + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.getEventSources = function () { + var state = this.getCurrentData(); + var sourceHash = state.eventSources; + var sourceApis = []; + for (var internalId in sourceHash) { + sourceApis.push(new EventSourceApi(state, sourceHash[internalId])); + } + return sourceApis; + }; + CalendarApi.prototype.getEventSourceById = function (id) { + var state = this.getCurrentData(); + var sourceHash = state.eventSources; + id = String(id); + for (var sourceId in sourceHash) { + if (sourceHash[sourceId].publicId === id) { + return new EventSourceApi(state, sourceHash[sourceId]); + } + } + return null; + }; + CalendarApi.prototype.addEventSource = function (sourceInput) { + var state = this.getCurrentData(); + if (sourceInput instanceof EventSourceApi) { + // not already present? don't want to add an old snapshot + if (!state.eventSources[sourceInput.internalEventSource.sourceId]) { + this.dispatch({ + type: 'ADD_EVENT_SOURCES', + sources: [sourceInput.internalEventSource] + }); + } + return sourceInput; + } + var eventSource = parseEventSource(sourceInput, state); + if (eventSource) { // TODO: error otherwise? + this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] }); + return new EventSourceApi(state, eventSource); + } + return null; + }; + CalendarApi.prototype.removeAllEventSources = function () { + this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' }); + }; + CalendarApi.prototype.refetchEvents = function () { + this.dispatch({ type: 'FETCH_EVENT_SOURCES' }); + }; + // Scroll + // ----------------------------------------------------------------------------------------------------------------- + CalendarApi.prototype.scrollToTime = function (timeInput) { + var time = createDuration(timeInput); + if (time) { + this.trigger('_scrollRequest', { time: time }); + } + }; + return CalendarApi; + }()); + + var EventApi = /** @class */ (function () { + // instance will be null if expressing a recurring event that has no current instances, + // OR if trying to validate an incoming external event that has no dates assigned + function EventApi(context, def, instance) { + this._context = context; + this._def = def; + this._instance = instance || null; + } + /* + TODO: make event struct more responsible for this + */ + EventApi.prototype.setProp = function (name, val) { + var _a, _b; + if (name in EVENT_DATE_REFINERS) { + console.warn("Could not set date-related prop 'name'. Use one of the date-related methods instead."); + } + else if (name in EVENT_NON_DATE_REFINERS) { + val = EVENT_NON_DATE_REFINERS[name](val); + this.mutate({ + standardProps: (_a = {}, _a[name] = val, _a) + }); + } + else if (name in EVENT_UI_REFINERS) { + var ui = EVENT_UI_REFINERS[name](val); + if (name === 'color') { + ui = { backgroundColor: val, borderColor: val }; + } + else if (name === 'editable') { + ui = { startEditable: val, durationEditable: val }; + } + else { + ui = (_b = {}, _b[name] = val, _b); + } + this.mutate({ + standardProps: { ui: ui } + }); + } + else { + console.warn("Could not set prop '" + name + "'. Use setExtendedProp instead."); + } + }; + EventApi.prototype.setExtendedProp = function (name, val) { + var _a; + this.mutate({ + extendedProps: (_a = {}, _a[name] = val, _a) + }); + }; + EventApi.prototype.setStart = function (startInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._context.dateEnv; + var start = dateEnv.createMarker(startInput); + if (start && this._instance) { // TODO: warning if parsed bad + var instanceRange = this._instance.range; + var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!? + if (options.maintainDuration) { + this.mutate({ datesDelta: startDelta }); + } + else { + this.mutate({ startDelta: startDelta }); + } + } + }; + EventApi.prototype.setEnd = function (endInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._context.dateEnv; + var end; + if (endInput != null) { + end = dateEnv.createMarker(endInput); + if (!end) { + return; // TODO: warning if parsed bad + } + } + if (this._instance) { + if (end) { + var endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity); + this.mutate({ endDelta: endDelta }); + } + else { + this.mutate({ standardProps: { hasEnd: false } }); + } + } + }; + EventApi.prototype.setDates = function (startInput, endInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._context.dateEnv; + var standardProps = { allDay: options.allDay }; + var start = dateEnv.createMarker(startInput); + var end; + if (!start) { + return; // TODO: warning if parsed bad + } + if (endInput != null) { + end = dateEnv.createMarker(endInput); + if (!end) { // TODO: warning if parsed bad + return; + } + } + if (this._instance) { + var instanceRange = this._instance.range; + // when computing the diff for an event being converted to all-day, + // compute diff off of the all-day values the way event-mutation does. + if (options.allDay === true) { + instanceRange = computeAlignedDayRange(instanceRange); + } + var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); + if (end) { + var endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity); + if (durationsEqual(startDelta, endDelta)) { + this.mutate({ datesDelta: startDelta, standardProps: standardProps }); + } + else { + this.mutate({ startDelta: startDelta, endDelta: endDelta, standardProps: standardProps }); + } + } + else { // means "clear the end" + standardProps.hasEnd = false; + this.mutate({ datesDelta: startDelta, standardProps: standardProps }); + } + } + }; + EventApi.prototype.moveStart = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ startDelta: delta }); + } + }; + EventApi.prototype.moveEnd = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ endDelta: delta }); + } + }; + EventApi.prototype.moveDates = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ datesDelta: delta }); + } + }; + EventApi.prototype.setAllDay = function (allDay, options) { + if (options === void 0) { options = {}; } + var standardProps = { allDay: allDay }; + var maintainDuration = options.maintainDuration; + if (maintainDuration == null) { + maintainDuration = this._context.options.allDayMaintainDuration; + } + if (this._def.allDay !== allDay) { + standardProps.hasEnd = maintainDuration; + } + this.mutate({ standardProps: standardProps }); + }; + EventApi.prototype.formatRange = function (formatInput) { + var dateEnv = this._context.dateEnv; + var instance = this._instance; + var formatter = createFormatter(formatInput); + if (this._def.hasEnd) { + return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, { + forcedStartTzo: instance.forcedStartTzo, + forcedEndTzo: instance.forcedEndTzo + }); + } + else { + return dateEnv.format(instance.range.start, formatter, { + forcedTzo: instance.forcedStartTzo + }); + } + }; + EventApi.prototype.mutate = function (mutation) { + var instance = this._instance; + if (instance) { + var def = this._def; + var context_1 = this._context; + var eventStore = context_1.getCurrentData().eventStore; + var relevantEvents_1 = getRelevantEvents(eventStore, instance.instanceId); + var eventConfigBase = { + '': { + display: '', + startEditable: true, + durationEditable: true, + constraints: [], + overlap: null, + allows: [], + backgroundColor: '', + borderColor: '', + textColor: '', + classNames: [] + } + }; + relevantEvents_1 = applyMutationToEventStore(relevantEvents_1, eventConfigBase, mutation, context_1); + var oldEvent = new EventApi(context_1, def, instance); // snapshot + this._def = relevantEvents_1.defs[def.defId]; + this._instance = relevantEvents_1.instances[instance.instanceId]; + context_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents_1 + }); + context_1.emitter.trigger('eventChange', { + oldEvent: oldEvent, + event: this, + relatedEvents: buildEventApis(relevantEvents_1, context_1, instance), + revert: function () { + context_1.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: relevantEvents_1 + }); + } + }); + } + }; + EventApi.prototype.remove = function () { + var context = this._context; + var asStore = eventApiToStore(this); + context.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: asStore + }); + context.emitter.trigger('eventRemove', { + event: this, + relatedEvents: [], + revert: function () { + context.dispatch({ + type: 'MERGE_EVENTS', + eventStore: asStore + }); + } + }); + }; + Object.defineProperty(EventApi.prototype, "source", { + get: function () { + var sourceId = this._def.sourceId; + if (sourceId) { + return new EventSourceApi(this._context, this._context.getCurrentData().eventSources[sourceId]); + } + return null; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "start", { + get: function () { + return this._instance ? + this._context.dateEnv.toDate(this._instance.range.start) : + null; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "end", { + get: function () { + return (this._instance && this._def.hasEnd) ? + this._context.dateEnv.toDate(this._instance.range.end) : + null; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "startStr", { + get: function () { + var instance = this._instance; + if (instance) { + return this._context.dateEnv.formatIso(instance.range.start, { + omitTime: this._def.allDay, + forcedTzo: instance.forcedStartTzo + }); + } + return ''; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "endStr", { + get: function () { + var instance = this._instance; + if (instance && this._def.hasEnd) { + return this._context.dateEnv.formatIso(instance.range.end, { + omitTime: this._def.allDay, + forcedTzo: instance.forcedEndTzo + }); + } + return ''; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "id", { + // computable props that all access the def + // TODO: find a TypeScript-compatible way to do this at scale + get: function () { return this._def.publicId; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "groupId", { + get: function () { return this._def.groupId; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "allDay", { + get: function () { return this._def.allDay; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "title", { + get: function () { return this._def.title; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "url", { + get: function () { return this._def.url; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "display", { + get: function () { return this._def.ui.display || 'auto'; } // bad. just normalize the type earlier + , + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "startEditable", { + get: function () { return this._def.ui.startEditable; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "durationEditable", { + get: function () { return this._def.ui.durationEditable; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "constraint", { + get: function () { return this._def.ui.constraints[0] || null; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "overlap", { + get: function () { return this._def.ui.overlap; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "allow", { + get: function () { return this._def.ui.allows[0] || null; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "backgroundColor", { + get: function () { return this._def.ui.backgroundColor; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "borderColor", { + get: function () { return this._def.ui.borderColor; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "textColor", { + get: function () { return this._def.ui.textColor; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "classNames", { + // NOTE: user can't modify these because Object.freeze was called in event-def parsing + get: function () { return this._def.ui.classNames; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "extendedProps", { + get: function () { return this._def.extendedProps; }, + enumerable: false, + configurable: true + }); + EventApi.prototype.toPlainObject = function (settings) { + if (settings === void 0) { settings = {}; } + var def = this._def; + var ui = def.ui; + var _a = this, startStr = _a.startStr, endStr = _a.endStr; + var res = {}; + if (def.title) { + res.title = def.title; + } + if (startStr) { + res.start = startStr; + } + if (endStr) { + res.end = endStr; + } + if (def.publicId) { + res.id = def.publicId; + } + if (def.groupId) { + res.groupId = def.groupId; + } + if (def.url) { + res.url = def.url; + } + if (ui.display && ui.display !== 'auto') { + res.display = ui.display; + } + // TODO: what about recurring-event properties??? + // TODO: include startEditable/durationEditable/constraint/overlap/allow + if (settings.collapseColor && ui.backgroundColor && ui.backgroundColor === ui.borderColor) { + res.color = ui.backgroundColor; + } + else { + if (ui.backgroundColor) { + res.backgroundColor = ui.backgroundColor; + } + if (ui.borderColor) { + res.borderColor = ui.borderColor; + } + } + if (ui.textColor) { + res.textColor = ui.textColor; + } + if (ui.classNames.length) { + res.classNames = ui.classNames; + } + if (Object.keys(def.extendedProps).length) { + if (settings.collapseExtendedProps) { + __assign(res, def.extendedProps); + } + else { + res.extendedProps = def.extendedProps; + } + } + return res; + }; + EventApi.prototype.toJSON = function () { + return this.toPlainObject(); + }; + return EventApi; + }()); + function eventApiToStore(eventApi) { + var _a, _b; + var def = eventApi._def; + var instance = eventApi._instance; + return { + defs: (_a = {}, _a[def.defId] = def, _a), + instances: instance + ? (_b = {}, _b[instance.instanceId] = instance, _b) : {} + }; + } + function buildEventApis(eventStore, context, excludeInstance) { + var defs = eventStore.defs, instances = eventStore.instances; + var eventApis = []; + var excludeInstanceId = excludeInstance ? excludeInstance.instanceId : ''; + for (var id in instances) { + var instance = instances[id]; + var def = defs[instance.defId]; + if (instance.instanceId !== excludeInstanceId) { + eventApis.push(new EventApi(context, def, instance)); + } + } + return eventApis; + } + + var calendarSystemClassMap = {}; + function registerCalendarSystem(name, theClass) { + calendarSystemClassMap[name] = theClass; + } + function createCalendarSystem(name) { + return new calendarSystemClassMap[name](); + } + var GregorianCalendarSystem = /** @class */ (function () { + function GregorianCalendarSystem() { + } + GregorianCalendarSystem.prototype.getMarkerYear = function (d) { + return d.getUTCFullYear(); + }; + GregorianCalendarSystem.prototype.getMarkerMonth = function (d) { + return d.getUTCMonth(); + }; + GregorianCalendarSystem.prototype.getMarkerDay = function (d) { + return d.getUTCDate(); + }; + GregorianCalendarSystem.prototype.arrayToMarker = function (arr) { + return arrayToUtcDate(arr); + }; + GregorianCalendarSystem.prototype.markerToArray = function (marker) { + return dateToUtcArray(marker); + }; + return GregorianCalendarSystem; + }()); + registerCalendarSystem('gregory', GregorianCalendarSystem); + + var ISO_RE = /^\s*(\d{4})(-?(\d{2})(-?(\d{2})([T ](\d{2}):?(\d{2})(:?(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/; + function parse(str) { + var m = ISO_RE.exec(str); + if (m) { + var marker = new Date(Date.UTC(Number(m[1]), m[3] ? Number(m[3]) - 1 : 0, Number(m[5] || 1), Number(m[7] || 0), Number(m[8] || 0), Number(m[10] || 0), m[12] ? Number('0.' + m[12]) * 1000 : 0)); + if (isValidDate(marker)) { + var timeZoneOffset = null; + if (m[13]) { + timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 + + Number(m[18] || 0)); + } + return { + marker: marker, + isTimeUnspecified: !m[6], + timeZoneOffset: timeZoneOffset + }; + } + } + return null; + } + + var DateEnv = /** @class */ (function () { + function DateEnv(settings) { + var timeZone = this.timeZone = settings.timeZone; + var isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC'; + if (settings.namedTimeZoneImpl && isNamedTimeZone) { + this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone); + } + this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl); + this.calendarSystem = createCalendarSystem(settings.calendarSystem); + this.locale = settings.locale; + this.weekDow = settings.locale.week.dow; + this.weekDoy = settings.locale.week.doy; + if (settings.weekNumberCalculation === 'ISO') { + this.weekDow = 1; + this.weekDoy = 4; + } + if (typeof settings.firstDay === 'number') { + this.weekDow = settings.firstDay; + } + if (typeof settings.weekNumberCalculation === 'function') { + this.weekNumberFunc = settings.weekNumberCalculation; + } + this.weekText = settings.weekText != null ? settings.weekText : settings.locale.options.weekText; + this.cmdFormatter = settings.cmdFormatter; + this.defaultSeparator = settings.defaultSeparator; + } + // Creating / Parsing + DateEnv.prototype.createMarker = function (input) { + var meta = this.createMarkerMeta(input); + if (meta === null) { + return null; + } + return meta.marker; + }; + DateEnv.prototype.createNowMarker = function () { + if (this.canComputeOffset) { + return this.timestampToMarker(new Date().valueOf()); + } + else { + // if we can't compute the current date val for a timezone, + // better to give the current local date vals than UTC + return arrayToUtcDate(dateToLocalArray(new Date())); + } + }; + DateEnv.prototype.createMarkerMeta = function (input) { + if (typeof input === 'string') { + return this.parse(input); + } + var marker = null; + if (typeof input === 'number') { + marker = this.timestampToMarker(input); + } + else if (input instanceof Date) { + input = input.valueOf(); + if (!isNaN(input)) { + marker = this.timestampToMarker(input); + } + } + else if (Array.isArray(input)) { + marker = arrayToUtcDate(input); + } + if (marker === null || !isValidDate(marker)) { + return null; + } + return { marker: marker, isTimeUnspecified: false, forcedTzo: null }; + }; + DateEnv.prototype.parse = function (s) { + var parts = parse(s); + if (parts === null) { + return null; + } + var marker = parts.marker; + var forcedTzo = null; + if (parts.timeZoneOffset !== null) { + if (this.canComputeOffset) { + marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000); + } + else { + forcedTzo = parts.timeZoneOffset; + } + } + return { marker: marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo: forcedTzo }; + }; + // Accessors + DateEnv.prototype.getYear = function (marker) { + return this.calendarSystem.getMarkerYear(marker); + }; + DateEnv.prototype.getMonth = function (marker) { + return this.calendarSystem.getMarkerMonth(marker); + }; + // Adding / Subtracting + DateEnv.prototype.add = function (marker, dur) { + var a = this.calendarSystem.markerToArray(marker); + a[0] += dur.years; + a[1] += dur.months; + a[2] += dur.days; + a[6] += dur.milliseconds; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.subtract = function (marker, dur) { + var a = this.calendarSystem.markerToArray(marker); + a[0] -= dur.years; + a[1] -= dur.months; + a[2] -= dur.days; + a[6] -= dur.milliseconds; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.addYears = function (marker, n) { + var a = this.calendarSystem.markerToArray(marker); + a[0] += n; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.addMonths = function (marker, n) { + var a = this.calendarSystem.markerToArray(marker); + a[1] += n; + return this.calendarSystem.arrayToMarker(a); + }; + // Diffing Whole Units + DateEnv.prototype.diffWholeYears = function (m0, m1) { + var calendarSystem = this.calendarSystem; + if (timeAsMs(m0) === timeAsMs(m1) && + calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) && + calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) { + return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0); + } + return null; + }; + DateEnv.prototype.diffWholeMonths = function (m0, m1) { + var calendarSystem = this.calendarSystem; + if (timeAsMs(m0) === timeAsMs(m1) && + calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) { + return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) + + (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12; + } + return null; + }; + // Range / Duration + DateEnv.prototype.greatestWholeUnit = function (m0, m1) { + var n = this.diffWholeYears(m0, m1); + if (n !== null) { + return { unit: 'year', value: n }; + } + n = this.diffWholeMonths(m0, m1); + if (n !== null) { + return { unit: 'month', value: n }; + } + n = diffWholeWeeks(m0, m1); + if (n !== null) { + return { unit: 'week', value: n }; + } + n = diffWholeDays(m0, m1); + if (n !== null) { + return { unit: 'day', value: n }; + } + n = diffHours(m0, m1); + if (isInt(n)) { + return { unit: 'hour', value: n }; + } + n = diffMinutes(m0, m1); + if (isInt(n)) { + return { unit: 'minute', value: n }; + } + n = diffSeconds(m0, m1); + if (isInt(n)) { + return { unit: 'second', value: n }; + } + return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() }; + }; + DateEnv.prototype.countDurationsBetween = function (m0, m1, d) { + // TODO: can use greatestWholeUnit + var diff; + if (d.years) { + diff = this.diffWholeYears(m0, m1); + if (diff !== null) { + return diff / asRoughYears(d); + } + } + if (d.months) { + diff = this.diffWholeMonths(m0, m1); + if (diff !== null) { + return diff / asRoughMonths(d); + } + } + if (d.days) { + diff = diffWholeDays(m0, m1); + if (diff !== null) { + return diff / asRoughDays(d); + } + } + return (m1.valueOf() - m0.valueOf()) / asRoughMs(d); + }; + // Start-Of + // these DON'T return zoned-dates. only UTC start-of dates + DateEnv.prototype.startOf = function (m, unit) { + if (unit === 'year') { + return this.startOfYear(m); + } + else if (unit === 'month') { + return this.startOfMonth(m); + } + else if (unit === 'week') { + return this.startOfWeek(m); + } + else if (unit === 'day') { + return startOfDay(m); + } + else if (unit === 'hour') { + return startOfHour(m); + } + else if (unit === 'minute') { + return startOfMinute(m); + } + else if (unit === 'second') { + return startOfSecond(m); + } + }; + DateEnv.prototype.startOfYear = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m) + ]); + }; + DateEnv.prototype.startOfMonth = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m), + this.calendarSystem.getMarkerMonth(m) + ]); + }; + DateEnv.prototype.startOfWeek = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m), + this.calendarSystem.getMarkerMonth(m), + m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7) + ]); + }; + // Week Number + DateEnv.prototype.computeWeekNumber = function (marker) { + if (this.weekNumberFunc) { + return this.weekNumberFunc(this.toDate(marker)); + } + else { + return weekOfYear(marker, this.weekDow, this.weekDoy); + } + }; + // TODO: choke on timeZoneName: long + DateEnv.prototype.format = function (marker, formatter, dateOptions) { + if (dateOptions === void 0) { dateOptions = {}; } + return formatter.format({ + marker: marker, + timeZoneOffset: dateOptions.forcedTzo != null ? + dateOptions.forcedTzo : + this.offsetForMarker(marker) + }, this); + }; + DateEnv.prototype.formatRange = function (start, end, formatter, dateOptions) { + if (dateOptions === void 0) { dateOptions = {}; } + if (dateOptions.isEndExclusive) { + end = addMs(end, -1); + } + return formatter.formatRange({ + marker: start, + timeZoneOffset: dateOptions.forcedStartTzo != null ? + dateOptions.forcedStartTzo : + this.offsetForMarker(start) + }, { + marker: end, + timeZoneOffset: dateOptions.forcedEndTzo != null ? + dateOptions.forcedEndTzo : + this.offsetForMarker(end) + }, this, dateOptions.defaultSeparator); + }; + /* + DUMB: the omitTime arg is dumb. if we omit the time, we want to omit the timezone offset. and if we do that, + might as well use buildIsoString or some other util directly + */ + DateEnv.prototype.formatIso = function (marker, extraOptions) { + if (extraOptions === void 0) { extraOptions = {}; } + var timeZoneOffset = null; + if (!extraOptions.omitTimeZoneOffset) { + if (extraOptions.forcedTzo != null) { + timeZoneOffset = extraOptions.forcedTzo; + } + else { + timeZoneOffset = this.offsetForMarker(marker); + } + } + return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime); + }; + // TimeZone + DateEnv.prototype.timestampToMarker = function (ms) { + if (this.timeZone === 'local') { + return arrayToUtcDate(dateToLocalArray(new Date(ms))); + } + else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) { + return new Date(ms); + } + else { + return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms)); + } + }; + DateEnv.prototype.offsetForMarker = function (m) { + if (this.timeZone === 'local') { + return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset + } + else if (this.timeZone === 'UTC') { + return 0; + } + else if (this.namedTimeZoneImpl) { + return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)); + } + return null; + }; + // Conversion + DateEnv.prototype.toDate = function (m, forcedTzo) { + if (this.timeZone === 'local') { + return arrayToLocalDate(dateToUtcArray(m)); + } + else if (this.timeZone === 'UTC') { + return new Date(m.valueOf()); // make sure it's a copy + } + else if (!this.namedTimeZoneImpl) { + return new Date(m.valueOf() - (forcedTzo || 0)); + } + else { + return new Date(m.valueOf() - + this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60 // convert minutes -> ms + ); + } + }; + return DateEnv; + }()); + + var globalLocales = []; + + var RAW_EN_LOCALE = { + code: 'en', + week: { + dow: 0, + doy: 4 // 4 days need to be within the year to be considered the first week + }, + direction: 'ltr', + buttonText: { + prev: 'prev', + next: 'next', + prevYear: 'prev year', + nextYear: 'next year', + year: 'year', + today: 'today', + month: 'month', + week: 'week', + day: 'day', + list: 'list' + }, + weekText: 'W', + allDayText: 'all-day', + moreLinkText: 'more', + noEventsText: 'No events to display' + }; + function organizeRawLocales(explicitRawLocales) { + var defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en'; + var allRawLocales = globalLocales.concat(explicitRawLocales); + var rawLocaleMap = { + en: RAW_EN_LOCALE // necessary? + }; + for (var _i = 0, allRawLocales_1 = allRawLocales; _i < allRawLocales_1.length; _i++) { + var rawLocale = allRawLocales_1[_i]; + rawLocaleMap[rawLocale.code] = rawLocale; + } + return { + map: rawLocaleMap, + defaultCode: defaultCode + }; + } + function buildLocale(inputSingular, available) { + if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) { + return parseLocale(inputSingular.code, [inputSingular.code], inputSingular); + } + else { + return queryLocale(inputSingular, available); + } + } + function queryLocale(codeArg, available) { + var codes = [].concat(codeArg || []); // will convert to array + var raw = queryRawLocale(codes, available) || RAW_EN_LOCALE; + return parseLocale(codeArg, codes, raw); + } + function queryRawLocale(codes, available) { + for (var i = 0; i < codes.length; i++) { + var parts = codes[i].toLocaleLowerCase().split('-'); + for (var j = parts.length; j > 0; j--) { + var simpleId = parts.slice(0, j).join('-'); + if (available[simpleId]) { + return available[simpleId]; + } + } + } + return null; + } + function parseLocale(codeArg, codes, raw) { + var merged = mergeProps([RAW_EN_LOCALE, raw], ['buttonText']); + delete merged.code; // don't want this part of the options + var week = merged.week; + delete merged.week; + return { + codeArg: codeArg, + codes: codes, + week: week, + simpleNumberFormat: new Intl.NumberFormat(codeArg), + options: merged + }; + } + + function formatDate(dateInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = buildDateEnv(options); + var formatter = createFormatter(options); + var dateMeta = dateEnv.createMarkerMeta(dateInput); + if (!dateMeta) { // TODO: warning? + return ''; + } + return dateEnv.format(dateMeta.marker, formatter, { + forcedTzo: dateMeta.forcedTzo + }); + } + function formatRange(startInput, endInput, options // mixture of env and formatter settings + ) { + var dateEnv = buildDateEnv(typeof options === 'object' && options ? options : {}); // pass in if non-null object + var formatter = createFormatter(options); + var startMeta = dateEnv.createMarkerMeta(startInput); + var endMeta = dateEnv.createMarkerMeta(endInput); + if (!startMeta || !endMeta) { // TODO: warning? + return ''; + } + return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, { + forcedStartTzo: startMeta.forcedTzo, + forcedEndTzo: endMeta.forcedTzo, + isEndExclusive: options.isEndExclusive, + defaultSeparator: BASE_OPTION_DEFAULTS.defaultRangeSeparator + }); + } + // TODO: more DRY and optimized + function buildDateEnv(settings) { + var locale = buildLocale(settings.locale || 'en', organizeRawLocales([]).map); // TODO: don't hardcode 'en' everywhere + return new DateEnv(__assign(__assign({ timeZone: BASE_OPTION_DEFAULTS.timeZone, calendarSystem: 'gregory' }, settings), { locale: locale })); + } + + var DEF_DEFAULTS = { + startTime: '09:00', + endTime: '17:00', + daysOfWeek: [1, 2, 3, 4, 5], + display: 'inverse-background', + classNames: 'fc-non-business', + groupId: '_businessHours' // so multiple defs get grouped + }; + /* + TODO: pass around as EventDefHash!!! + */ + function parseBusinessHours(input, context) { + return parseEvents(refineInputs(input), null, context); + } + function refineInputs(input) { + var rawDefs; + if (input === true) { + rawDefs = [{}]; // will get DEF_DEFAULTS verbatim + } + else if (Array.isArray(input)) { + // if specifying an array, every sub-definition NEEDS a day-of-week + rawDefs = input.filter(function (rawDef) { + return rawDef.daysOfWeek; + }); + } + else if (typeof input === 'object' && input) { // non-null object + rawDefs = [input]; + } + else { // is probably false + rawDefs = []; + } + rawDefs = rawDefs.map(function (rawDef) { + return __assign(__assign({}, DEF_DEFAULTS), rawDef); + }); + return rawDefs; + } + + function pointInsideRect(point, rect) { + return point.left >= rect.left && + point.left < rect.right && + point.top >= rect.top && + point.top < rect.bottom; + } + // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false + function intersectRects(rect1, rect2) { + var res = { + left: Math.max(rect1.left, rect2.left), + right: Math.min(rect1.right, rect2.right), + top: Math.max(rect1.top, rect2.top), + bottom: Math.min(rect1.bottom, rect2.bottom) + }; + if (res.left < res.right && res.top < res.bottom) { + return res; + } + return false; + } + function translateRect(rect, deltaX, deltaY) { + return { + left: rect.left + deltaX, + right: rect.right + deltaX, + top: rect.top + deltaY, + bottom: rect.bottom + deltaY + }; + } + // Returns a new point that will have been moved to reside within the given rectangle + function constrainPoint(point, rect) { + return { + left: Math.min(Math.max(point.left, rect.left), rect.right), + top: Math.min(Math.max(point.top, rect.top), rect.bottom) + }; + } + // Returns a point that is the center of the given rectangle + function getRectCenter(rect) { + return { + left: (rect.left + rect.right) / 2, + top: (rect.top + rect.bottom) / 2 + }; + } + // Subtracts point2's coordinates from point1's coordinates, returning a delta + function diffPoints(point1, point2) { + return { + left: point1.left - point2.left, + top: point1.top - point2.top + }; + } + + var canVGrowWithinCell; + function getCanVGrowWithinCell() { + if (canVGrowWithinCell == null) { + canVGrowWithinCell = computeCanVGrowWithinCell(); + } + return canVGrowWithinCell; + } + function computeCanVGrowWithinCell() { + // for SSR, because this function is call immediately at top-level + // TODO: just make this logic execute top-level, immediately, instead of doing lazily + if (typeof document === 'undefined') { + return true; + } + var el = document.createElement('div'); + el.style.position = 'absolute'; + el.style.top = '0px'; + el.style.left = '0px'; + el.innerHTML = '
'; + el.querySelector('table').style.height = '100px'; + el.querySelector('div').style.height = '100%'; + document.body.appendChild(el); + var div = el.querySelector('div'); + var possible = div.offsetHeight > 0; + document.body.removeChild(el); + return possible; + } + + var EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere + var Splitter = /** @class */ (function () { + function Splitter() { + this.getKeysForEventDefs = memoize(this._getKeysForEventDefs); + this.splitDateSelection = memoize(this._splitDateSpan); + this.splitEventStore = memoize(this._splitEventStore); + this.splitIndividualUi = memoize(this._splitIndividualUi); + this.splitEventDrag = memoize(this._splitInteraction); + this.splitEventResize = memoize(this._splitInteraction); + this.eventUiBuilders = {}; // TODO: typescript protection + } + Splitter.prototype.splitProps = function (props) { + var _this = this; + var keyInfos = this.getKeyInfo(props); + var defKeys = this.getKeysForEventDefs(props.eventStore); + var dateSelections = this.splitDateSelection(props.dateSelection); + var individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases* + var eventStores = this.splitEventStore(props.eventStore, defKeys); + var eventDrags = this.splitEventDrag(props.eventDrag); + var eventResizes = this.splitEventResize(props.eventResize); + var splitProps = {}; + this.eventUiBuilders = mapHash(keyInfos, function (info, key) { + return _this.eventUiBuilders[key] || memoize(buildEventUiForKey); + }); + for (var key in keyInfos) { + var keyInfo = keyInfos[key]; + var eventStore = eventStores[key] || EMPTY_EVENT_STORE; + var buildEventUi = this.eventUiBuilders[key]; + splitProps[key] = { + businessHours: keyInfo.businessHours || props.businessHours, + dateSelection: dateSelections[key] || null, + eventStore: eventStore, + eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]), + eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '', + eventDrag: eventDrags[key] || null, + eventResize: eventResizes[key] || null + }; + } + return splitProps; + }; + Splitter.prototype._splitDateSpan = function (dateSpan) { + var dateSpans = {}; + if (dateSpan) { + var keys = this.getKeysForDateSpan(dateSpan); + for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { + var key = keys_1[_i]; + dateSpans[key] = dateSpan; + } + } + return dateSpans; + }; + Splitter.prototype._getKeysForEventDefs = function (eventStore) { + var _this = this; + return mapHash(eventStore.defs, function (eventDef) { + return _this.getKeysForEventDef(eventDef); + }); + }; + Splitter.prototype._splitEventStore = function (eventStore, defKeys) { + var defs = eventStore.defs, instances = eventStore.instances; + var splitStores = {}; + for (var defId in defs) { + for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { + var key = _a[_i]; + if (!splitStores[key]) { + splitStores[key] = createEmptyEventStore(); + } + splitStores[key].defs[defId] = defs[defId]; + } + } + for (var instanceId in instances) { + var instance = instances[instanceId]; + for (var _b = 0, _c = defKeys[instance.defId]; _b < _c.length; _b++) { + var key = _c[_b]; + if (splitStores[key]) { // must have already been created + splitStores[key].instances[instanceId] = instance; + } + } + } + return splitStores; + }; + Splitter.prototype._splitIndividualUi = function (eventUiBases, defKeys) { + var splitHashes = {}; + for (var defId in eventUiBases) { + if (defId) { // not the '' key + for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { + var key = _a[_i]; + if (!splitHashes[key]) { + splitHashes[key] = {}; + } + splitHashes[key][defId] = eventUiBases[defId]; + } + } + } + return splitHashes; + }; + Splitter.prototype._splitInteraction = function (interaction) { + var splitStates = {}; + if (interaction) { + var affectedStores_1 = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents) // can't use cached. might be events from other calendar + ); + // can't rely on defKeys because event data is mutated + var mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents); + var mutatedStores_1 = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId); + var populate = function (key) { + if (!splitStates[key]) { + splitStates[key] = { + affectedEvents: affectedStores_1[key] || EMPTY_EVENT_STORE, + mutatedEvents: mutatedStores_1[key] || EMPTY_EVENT_STORE, + isEvent: interaction.isEvent + }; + } + }; + for (var key in affectedStores_1) { + populate(key); + } + for (var key in mutatedStores_1) { + populate(key); + } + } + return splitStates; + }; + return Splitter; + }()); + function buildEventUiForKey(allUi, eventUiForKey, individualUi) { + var baseParts = []; + if (allUi) { + baseParts.push(allUi); + } + if (eventUiForKey) { + baseParts.push(eventUiForKey); + } + var stuff = { + '': combineEventUis(baseParts) + }; + if (individualUi) { + __assign(stuff, individualUi); + } + return stuff; + } + + function getDateMeta(date, todayRange, nowDate, dateProfile) { + return { + dow: date.getUTCDay(), + isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)), + isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)), + isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)), + isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false), + isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false) + }; + } + function getDayClassNames(meta, theme) { + var classNames = [ + 'fc-day', + 'fc-day-' + DAY_IDS[meta.dow] + ]; + if (meta.isDisabled) { + classNames.push('fc-day-disabled'); + } + else { + if (meta.isToday) { + classNames.push('fc-day-today'); + classNames.push(theme.getClass('today')); + } + if (meta.isPast) { + classNames.push('fc-day-past'); + } + if (meta.isFuture) { + classNames.push('fc-day-future'); + } + if (meta.isOther) { + classNames.push('fc-day-other'); + } + } + return classNames; + } + function getSlotClassNames(meta, theme) { + var classNames = [ + 'fc-slot', + 'fc-slot-' + DAY_IDS[meta.dow] + ]; + if (meta.isDisabled) { + classNames.push('fc-slot-disabled'); + } + else { + if (meta.isToday) { + classNames.push('fc-slot-today'); + classNames.push(theme.getClass('today')); + } + if (meta.isPast) { + classNames.push('fc-slot-past'); + } + if (meta.isFuture) { + classNames.push('fc-slot-future'); + } + } + return classNames; + } + + function buildNavLinkData(date, type) { + if (type === void 0) { type = 'day'; } + return JSON.stringify({ + date: formatDayString(date), + type: type + }); + } + + var _isRtlScrollbarOnLeft = null; + function getIsRtlScrollbarOnLeft() { + if (_isRtlScrollbarOnLeft === null) { + _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft(); + } + return _isRtlScrollbarOnLeft; + } + function computeIsRtlScrollbarOnLeft() { + var outerEl = document.createElement('div'); + applyStyle(outerEl, { + position: 'absolute', + top: -1000, + left: 0, + border: 0, + padding: 0, + overflow: 'scroll', + direction: 'rtl' + }); + outerEl.innerHTML = '
'; + document.body.appendChild(outerEl); + var innerEl = outerEl.firstChild; + var res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left; + removeElement(outerEl); + return res; + } + + var _scrollbarWidths; + function getScrollbarWidths() { + if (!_scrollbarWidths) { + _scrollbarWidths = computeScrollbarWidths(); + } + return _scrollbarWidths; + } + function computeScrollbarWidths() { + var el = document.createElement('div'); + el.style.overflow = 'scroll'; + document.body.appendChild(el); + var res = computeScrollbarWidthsForEl(el); + document.body.removeChild(el); + return res; + } + // WARNING: will include border + function computeScrollbarWidthsForEl(el) { + return { + x: el.offsetHeight - el.clientHeight, + y: el.offsetWidth - el.clientWidth + }; + } + + function computeEdges(el, getPadding) { + if (getPadding === void 0) { getPadding = false; } + var computedStyle = window.getComputedStyle(el); + var borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0; + var borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0; + var borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0; + var borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0; + var badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border! + var scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight; + var scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom; + var res = { + borderLeft: borderLeft, + borderRight: borderRight, + borderTop: borderTop, + borderBottom: borderBottom, + scrollbarBottom: scrollbarBottom, + scrollbarLeft: 0, + scrollbarRight: 0 + }; + if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side? + res.scrollbarLeft = scrollbarLeftRight; + } + else { + res.scrollbarRight = scrollbarLeftRight; + } + if (getPadding) { + res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0; + res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0; + res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0; + res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0; + } + return res; + } + function computeInnerRect(el, goWithinPadding, doFromWindowViewport) { + if (goWithinPadding === void 0) { goWithinPadding = false; } + var outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el); + var edges = computeEdges(el, goWithinPadding); + var res = { + left: outerRect.left + edges.borderLeft + edges.scrollbarLeft, + right: outerRect.right - edges.borderRight - edges.scrollbarRight, + top: outerRect.top + edges.borderTop, + bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom + }; + if (goWithinPadding) { + res.left += edges.paddingLeft; + res.right -= edges.paddingRight; + res.top += edges.paddingTop; + res.bottom -= edges.paddingBottom; + } + return res; + } + function computeRect(el) { + var rect = el.getBoundingClientRect(); + return { + left: rect.left + window.pageXOffset, + top: rect.top + window.pageYOffset, + right: rect.right + window.pageXOffset, + bottom: rect.bottom + window.pageYOffset + }; + } + function computeHeightAndMargins(el) { + return el.getBoundingClientRect().height + computeVMargins(el); + } + function computeVMargins(el) { + var computed = window.getComputedStyle(el); + return parseInt(computed.marginTop, 10) + + parseInt(computed.marginBottom, 10); + } + // does not return window + function getClippingParents(el) { + var parents = []; + while (el instanceof HTMLElement) { // will stop when gets to document or null + var computedStyle = window.getComputedStyle(el); + if (computedStyle.position === 'fixed') { + break; + } + if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) { + parents.push(el); + } + el = el.parentNode; + } + return parents; + } + + // given a function that resolves a result asynchronously. + // the function can either call passed-in success and failure callbacks, + // or it can return a promise. + // if you need to pass additional params to func, bind them first. + function unpromisify(func, success, failure) { + // guard against success/failure callbacks being called more than once + // and guard against a promise AND callback being used together. + var isResolved = false; + var wrappedSuccess = function () { + if (!isResolved) { + isResolved = true; + success.apply(this, arguments); + } + }; + var wrappedFailure = function () { + if (!isResolved) { + isResolved = true; + if (failure) { + failure.apply(this, arguments); + } + } + }; + var res = func(wrappedSuccess, wrappedFailure); + if (res && typeof res.then === 'function') { + res.then(wrappedSuccess, wrappedFailure); + } + } + + var Emitter = /** @class */ (function () { + function Emitter() { + this.handlers = {}; + this.thisContext = null; + } + Emitter.prototype.setThisContext = function (thisContext) { + this.thisContext = thisContext; + }; + Emitter.prototype.setOptions = function (options) { + this.options = options; + }; + Emitter.prototype.on = function (type, handler) { + addToHash(this.handlers, type, handler); + }; + Emitter.prototype.off = function (type, handler) { + removeFromHash(this.handlers, type, handler); + }; + Emitter.prototype.trigger = function (type) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + var attachedHandlers = this.handlers[type] || []; + var optionHandler = this.options && this.options[type]; + var handlers = [].concat(optionHandler || [], attachedHandlers); + for (var _a = 0, handlers_1 = handlers; _a < handlers_1.length; _a++) { + var handler = handlers_1[_a]; + handler.apply(this.thisContext, args); + } + }; + Emitter.prototype.hasHandlers = function (type) { + return (this.handlers[type] && this.handlers[type].length) || + (this.options && this.options[type]); + }; + return Emitter; + }()); + function addToHash(hash, type, handler) { + (hash[type] || (hash[type] = [])) + .push(handler); + } + function removeFromHash(hash, type, handler) { + if (handler) { + if (hash[type]) { + hash[type] = hash[type].filter(function (func) { + return func !== handler; + }); + } + } + else { + delete hash[type]; // remove all handler funcs for this type + } + } + + /* + Records offset information for a set of elements, relative to an origin element. + Can record the left/right OR the top/bottom OR both. + Provides methods for querying the cache by position. + */ + var PositionCache = /** @class */ (function () { + function PositionCache(originEl, els, isHorizontal, isVertical) { + this.els = els; + var originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left + if (isHorizontal) { + this.buildElHorizontals(originClientRect.left); + } + if (isVertical) { + this.buildElVerticals(originClientRect.top); + } + } + // Populates the left/right internal coordinate arrays + PositionCache.prototype.buildElHorizontals = function (originClientLeft) { + var lefts = []; + var rights = []; + for (var _i = 0, _a = this.els; _i < _a.length; _i++) { + var el = _a[_i]; + var rect = el.getBoundingClientRect(); + lefts.push(rect.left - originClientLeft); + rights.push(rect.right - originClientLeft); + } + this.lefts = lefts; + this.rights = rights; + }; + // Populates the top/bottom internal coordinate arrays + PositionCache.prototype.buildElVerticals = function (originClientTop) { + var tops = []; + var bottoms = []; + for (var _i = 0, _a = this.els; _i < _a.length; _i++) { + var el = _a[_i]; + var rect = el.getBoundingClientRect(); + tops.push(rect.top - originClientTop); + bottoms.push(rect.bottom - originClientTop); + } + this.tops = tops; + this.bottoms = bottoms; + }; + // Given a left offset (from document left), returns the index of the el that it horizontally intersects. + // If no intersection is made, returns undefined. + PositionCache.prototype.leftToIndex = function (leftPosition) { + var lefts = this.lefts; + var rights = this.rights; + var len = lefts.length; + var i; + for (i = 0; i < len; i++) { + if (leftPosition >= lefts[i] && leftPosition < rights[i]) { + return i; + } + } + }; + // Given a top offset (from document top), returns the index of the el that it vertically intersects. + // If no intersection is made, returns undefined. + PositionCache.prototype.topToIndex = function (topPosition) { + var tops = this.tops; + var bottoms = this.bottoms; + var len = tops.length; + var i; + for (i = 0; i < len; i++) { + if (topPosition >= tops[i] && topPosition < bottoms[i]) { + return i; + } + } + }; + // Gets the width of the element at the given index + PositionCache.prototype.getWidth = function (leftIndex) { + return this.rights[leftIndex] - this.lefts[leftIndex]; + }; + // Gets the height of the element at the given index + PositionCache.prototype.getHeight = function (topIndex) { + return this.bottoms[topIndex] - this.tops[topIndex]; + }; + return PositionCache; + }()); + + /* + An object for getting/setting scroll-related information for an element. + Internally, this is done very differently for window versus DOM element, + so this object serves as a common interface. + */ + var ScrollController = /** @class */ (function () { + function ScrollController() { + } + ScrollController.prototype.getMaxScrollTop = function () { + return this.getScrollHeight() - this.getClientHeight(); + }; + ScrollController.prototype.getMaxScrollLeft = function () { + return this.getScrollWidth() - this.getClientWidth(); + }; + ScrollController.prototype.canScrollVertically = function () { + return this.getMaxScrollTop() > 0; + }; + ScrollController.prototype.canScrollHorizontally = function () { + return this.getMaxScrollLeft() > 0; + }; + ScrollController.prototype.canScrollUp = function () { + return this.getScrollTop() > 0; + }; + ScrollController.prototype.canScrollDown = function () { + return this.getScrollTop() < this.getMaxScrollTop(); + }; + ScrollController.prototype.canScrollLeft = function () { + return this.getScrollLeft() > 0; + }; + ScrollController.prototype.canScrollRight = function () { + return this.getScrollLeft() < this.getMaxScrollLeft(); + }; + return ScrollController; + }()); + var ElementScrollController = /** @class */ (function (_super) { + __extends(ElementScrollController, _super); + function ElementScrollController(el) { + var _this = _super.call(this) || this; + _this.el = el; + return _this; + } + ElementScrollController.prototype.getScrollTop = function () { + return this.el.scrollTop; + }; + ElementScrollController.prototype.getScrollLeft = function () { + return this.el.scrollLeft; + }; + ElementScrollController.prototype.setScrollTop = function (top) { + this.el.scrollTop = top; + }; + ElementScrollController.prototype.setScrollLeft = function (left) { + this.el.scrollLeft = left; + }; + ElementScrollController.prototype.getScrollWidth = function () { + return this.el.scrollWidth; + }; + ElementScrollController.prototype.getScrollHeight = function () { + return this.el.scrollHeight; + }; + ElementScrollController.prototype.getClientHeight = function () { + return this.el.clientHeight; + }; + ElementScrollController.prototype.getClientWidth = function () { + return this.el.clientWidth; + }; + return ElementScrollController; + }(ScrollController)); + var WindowScrollController = /** @class */ (function (_super) { + __extends(WindowScrollController, _super); + function WindowScrollController() { + return _super !== null && _super.apply(this, arguments) || this; + } + WindowScrollController.prototype.getScrollTop = function () { + return window.pageYOffset; + }; + WindowScrollController.prototype.getScrollLeft = function () { + return window.pageXOffset; + }; + WindowScrollController.prototype.setScrollTop = function (n) { + window.scroll(window.pageXOffset, n); + }; + WindowScrollController.prototype.setScrollLeft = function (n) { + window.scroll(n, window.pageYOffset); + }; + WindowScrollController.prototype.getScrollWidth = function () { + return document.documentElement.scrollWidth; + }; + WindowScrollController.prototype.getScrollHeight = function () { + return document.documentElement.scrollHeight; + }; + WindowScrollController.prototype.getClientHeight = function () { + return document.documentElement.clientHeight; + }; + WindowScrollController.prototype.getClientWidth = function () { + return document.documentElement.clientWidth; + }; + return WindowScrollController; + }(ScrollController)); + + var Theme = /** @class */ (function () { + function Theme(calendarOptions) { + if (this.iconOverrideOption) { + this.setIconOverride(calendarOptions[this.iconOverrideOption]); + } + } + Theme.prototype.setIconOverride = function (iconOverrideHash) { + var iconClassesCopy; + var buttonName; + if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object + iconClassesCopy = __assign({}, this.iconClasses); + for (buttonName in iconOverrideHash) { + iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]); + } + this.iconClasses = iconClassesCopy; + } + else if (iconOverrideHash === false) { + this.iconClasses = {}; + } + }; + Theme.prototype.applyIconOverridePrefix = function (className) { + var prefix = this.iconOverridePrefix; + if (prefix && className.indexOf(prefix) !== 0) { // if not already present + className = prefix + className; + } + return className; + }; + Theme.prototype.getClass = function (key) { + return this.classes[key] || ''; + }; + Theme.prototype.getIconClass = function (buttonName, isRtl) { + var className; + if (isRtl && this.rtlIconClasses) { + className = this.rtlIconClasses[buttonName] || this.iconClasses[buttonName]; + } + else { + className = this.iconClasses[buttonName]; + } + if (className) { + return this.baseIconClass + ' ' + className; + } + return ''; + }; + Theme.prototype.getCustomButtonIconClass = function (customButtonProps) { + var className; + if (this.iconOverrideCustomButtonOption) { + className = customButtonProps[this.iconOverrideCustomButtonOption]; + if (className) { + return this.baseIconClass + ' ' + this.applyIconOverridePrefix(className); + } + } + return ''; + }; + return Theme; + }()); + Theme.prototype.classes = {}; + Theme.prototype.iconClasses = {}; + Theme.prototype.baseIconClass = ''; + Theme.prototype.iconOverridePrefix = ''; + + /// + if (typeof FullCalendarVDom === 'undefined') { + throw new Error('Please import the top-level fullcalendar lib before attempting to import a plugin.'); + } + var Component = FullCalendarVDom.Component; + var createElement = FullCalendarVDom.createElement; + var render = FullCalendarVDom.render; + var createRef = FullCalendarVDom.createRef; + var Fragment = FullCalendarVDom.Fragment; + var createContext$1 = FullCalendarVDom.createContext; + var flushToDom$1 = FullCalendarVDom.flushToDom; + + var ScrollResponder = /** @class */ (function () { + function ScrollResponder(execFunc, emitter, scrollTime) { + var _this = this; + this.execFunc = execFunc; + this.emitter = emitter; + this.scrollTime = scrollTime; + this.handleScrollRequest = function (request) { + _this.queuedRequest = __assign({}, _this.queuedRequest || {}, request); + _this.drain(); + }; + emitter.on('_scrollRequest', this.handleScrollRequest); + this.fireInitialScroll(); + } + ScrollResponder.prototype.detach = function () { + this.emitter.off('_scrollRequest', this.handleScrollRequest); + }; + ScrollResponder.prototype.update = function (isDatesNew) { + if (isDatesNew) { + this.fireInitialScroll(); // will drain + } + else { + this.drain(); + } + }; + ScrollResponder.prototype.fireInitialScroll = function () { + this.handleScrollRequest({ + time: this.scrollTime + }); + }; + ScrollResponder.prototype.drain = function () { + if (this.queuedRequest && this.execFunc(this.queuedRequest)) { + this.queuedRequest = null; + } + }; + return ScrollResponder; + }()); + + var ViewContextType = createContext$1({}); // for Components + function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator, dateEnv, theme, pluginHooks, dispatch, getCurrentData, emitter, calendarApi, registerInteractiveComponent, unregisterInteractiveComponent) { + return { + dateEnv: dateEnv, + options: viewOptions, + pluginHooks: pluginHooks, + emitter: emitter, + dispatch: dispatch, + getCurrentData: getCurrentData, + calendarApi: calendarApi, + viewSpec: viewSpec, + viewApi: viewApi, + dateProfileGenerator: dateProfileGenerator, + theme: theme, + isRtl: viewOptions.direction === 'rtl', + addResizeHandler: function (handler) { + emitter.on('_resize', handler); + }, + removeResizeHandler: function (handler) { + emitter.off('_resize', handler); + }, + createScrollResponder: function (execFunc) { + return new ScrollResponder(execFunc, emitter, createDuration(viewOptions.scrollTime)); + }, + registerInteractiveComponent: registerInteractiveComponent, + unregisterInteractiveComponent: unregisterInteractiveComponent + }; + } + + var PureComponent = /** @class */ (function (_super) { + __extends(PureComponent, _super); + function PureComponent() { + return _super !== null && _super.apply(this, arguments) || this; + } + PureComponent.prototype.shouldComponentUpdate = function (nextProps, nextState) { + if (this.debug) { + console.log(getUnequalProps(nextProps, this.props), getUnequalProps(nextState, this.state)); + } + return !compareObjs(this.props, nextProps, this.propEquality) || + !compareObjs(this.state, nextState, this.stateEquality); + }; + PureComponent.addPropsEquality = addPropsEquality; + PureComponent.addStateEquality = addStateEquality; + PureComponent.contextType = ViewContextType; + return PureComponent; + }(Component)); + PureComponent.prototype.propEquality = {}; + PureComponent.prototype.stateEquality = {}; + var BaseComponent = /** @class */ (function (_super) { + __extends(BaseComponent, _super); + function BaseComponent() { + return _super !== null && _super.apply(this, arguments) || this; + } + BaseComponent.contextType = ViewContextType; + return BaseComponent; + }(PureComponent)); + function addPropsEquality(propEquality) { + var hash = Object.create(this.prototype.propEquality); + __assign(hash, propEquality); + this.prototype.propEquality = hash; + } + function addStateEquality(stateEquality) { + var hash = Object.create(this.prototype.stateEquality); + __assign(hash, stateEquality); + this.prototype.stateEquality = hash; + } + // use other one + function setRef(ref, current) { + if (typeof ref === 'function') { + ref(current); + } + else if (ref) { + // see https://github.com/facebook/react/issues/13029 + ref.current = current; + } + } + + function reduceEventStore(eventStore, action, eventSources, dateProfile, context) { + switch (action.type) { + case 'RECEIVE_EVENTS': // raw + return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, context); + case 'ADD_EVENTS': // already parsed, but not expanded + return addEvent(eventStore, action.eventStore, // new ones + dateProfile ? dateProfile.activeRange : null, context); + case 'MERGE_EVENTS': // already parsed and expanded + return mergeEventStores(eventStore, action.eventStore); + case 'PREV': // TODO: how do we track all actions that affect dateProfile :( + case 'NEXT': + case 'CHANGE_DATE': + case 'CHANGE_VIEW_TYPE': + if (dateProfile) { + return expandRecurring(eventStore, dateProfile.activeRange, context); + } + else { + return eventStore; + } + case 'REMOVE_EVENTS': + return excludeSubEventStore(eventStore, action.eventStore); + case 'REMOVE_EVENT_SOURCE': + return excludeEventsBySourceId(eventStore, action.sourceId); + case 'REMOVE_ALL_EVENT_SOURCES': + return filterEventStoreDefs(eventStore, function (eventDef) { + return !eventDef.sourceId; // only keep events with no source id + }); + case 'REMOVE_ALL_EVENTS': + return createEmptyEventStore(); + default: + return eventStore; + } + } + function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, context) { + if (eventSource && // not already removed + fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources + ) { + var subset = parseEvents(transformRawEvents(rawEvents, eventSource, context), eventSource, context); + if (fetchRange) { + subset = expandRecurring(subset, fetchRange, context); + } + return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset); + } + return eventStore; + } + function transformRawEvents(rawEvents, eventSource, context) { + var calEachTransform = context.options.eventDataTransform; + var sourceEachTransform = eventSource ? eventSource.eventDataTransform : null; + if (sourceEachTransform) { + rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform); + } + if (calEachTransform) { + rawEvents = transformEachRawEvent(rawEvents, calEachTransform); + } + return rawEvents; + } + function transformEachRawEvent(rawEvents, func) { + var refinedEvents; + if (!func) { + refinedEvents = rawEvents; + } + else { + refinedEvents = []; + for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) { + var rawEvent = rawEvents_1[_i]; + var refinedEvent = func(rawEvent); + if (refinedEvent) { + refinedEvents.push(refinedEvent); + } + else if (refinedEvent == null) { + refinedEvents.push(rawEvent); + } // if a different falsy value, do nothing + } + } + return refinedEvents; + } + function addEvent(eventStore, subset, expandRange, context) { + if (expandRange) { + subset = expandRecurring(subset, expandRange, context); + } + return mergeEventStores(eventStore, subset); + } + function rezoneEventStoreDates(eventStore, oldDateEnv, newDateEnv) { + var defs = eventStore.defs; + var instances = mapHash(eventStore.instances, function (instance) { + var def = defs[instance.defId]; + if (def.allDay || def.recurringDef) { + return instance; // isn't dependent on timezone + } + else { + return __assign(__assign({}, instance), { range: { + start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)), + end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo)) + }, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo }); + } + }); + return { defs: defs, instances: instances }; + } + function excludeEventsBySourceId(eventStore, sourceId) { + return filterEventStoreDefs(eventStore, function (eventDef) { + return eventDef.sourceId !== sourceId; + }); + } + // QUESTION: why not just return instances? do a general object-property-exclusion util + function excludeInstances(eventStore, removals) { + return { + defs: eventStore.defs, + instances: filterHash(eventStore.instances, function (instance) { + return !removals[instance.instanceId]; + }) + }; + } + + // high-level segmenting-aware tester functions + // ------------------------------------------------------------------------------------------------------------------------ + function isInteractionValid(interaction, context) { + return isNewPropsValid({ eventDrag: interaction }, context); // HACK: the eventDrag props is used for ALL interactions + } + function isDateSelectionValid(dateSelection, context) { + return isNewPropsValid({ dateSelection: dateSelection }, context); + } + function isNewPropsValid(newProps, context) { + var calendarState = context.getCurrentData(); + var props = __assign({ businessHours: calendarState.businessHours, dateSelection: '', eventStore: calendarState.eventStore, eventUiBases: calendarState.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps); + return (context.pluginHooks.isPropsValid || isPropsValid)(props, context); + } + function isPropsValid(state, context, dateSpanMeta, filterConfig) { + if (dateSpanMeta === void 0) { dateSpanMeta = {}; } + if (state.eventDrag && !isInteractionPropsValid(state, context, dateSpanMeta, filterConfig)) { + return false; + } + if (state.dateSelection && !isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig)) { + return false; + } + return true; + } + // Moving Event Validation + // ------------------------------------------------------------------------------------------------------------------------ + function isInteractionPropsValid(state, context, dateSpanMeta, filterConfig) { + var currentState = context.getCurrentData(); + var interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions + var subjectEventStore = interaction.mutatedEvents; + var subjectDefs = subjectEventStore.defs; + var subjectInstances = subjectEventStore.instances; + var subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ? + state.eventUiBases : + { '': currentState.selectionConfig } // if not a real event, validate as a selection + ); + if (filterConfig) { + subjectConfigs = mapHash(subjectConfigs, filterConfig); + } + var otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances); // exclude the subject events. TODO: exclude defs too? + var otherDefs = otherEventStore.defs; + var otherInstances = otherEventStore.instances; + var otherConfigs = compileEventUis(otherDefs, state.eventUiBases); + for (var subjectInstanceId in subjectInstances) { + var subjectInstance = subjectInstances[subjectInstanceId]; + var subjectRange = subjectInstance.range; + var subjectConfig = subjectConfigs[subjectInstance.defId]; + var subjectDef = subjectDefs[subjectInstance.defId]; + // constraint + if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, context)) { + return false; + } + // overlap + var eventOverlap = context.options.eventOverlap; + var eventOverlapFunc = typeof eventOverlap === 'function' ? eventOverlap : null; + for (var otherInstanceId in otherInstances) { + var otherInstance = otherInstances[otherInstanceId]; + // intersect! evaluate + if (rangesIntersect(subjectRange, otherInstance.range)) { + var otherOverlap = otherConfigs[otherInstance.defId].overlap; + // consider the other event's overlap. only do this if the subject event is a "real" event + if (otherOverlap === false && interaction.isEvent) { + return false; + } + if (subjectConfig.overlap === false) { + return false; + } + if (eventOverlapFunc && !eventOverlapFunc(new EventApi(context, otherDefs[otherInstance.defId], otherInstance), // still event + new EventApi(context, subjectDef, subjectInstance) // moving event + )) { + return false; + } + } + } + // allow (a function) + var calendarEventStore = currentState.eventStore; // need global-to-calendar, not local to component (splittable)state + for (var _i = 0, _a = subjectConfig.allows; _i < _a.length; _i++) { + var subjectAllow = _a[_i]; + var subjectDateSpan = __assign(__assign({}, dateSpanMeta), { range: subjectInstance.range, allDay: subjectDef.allDay }); + var origDef = calendarEventStore.defs[subjectDef.defId]; + var origInstance = calendarEventStore.instances[subjectInstanceId]; + var eventApi = void 0; + if (origDef) { // was previously in the calendar + eventApi = new EventApi(context, origDef, origInstance); + } + else { // was an external event + eventApi = new EventApi(context, subjectDef); // no instance, because had no dates + } + if (!subjectAllow(buildDateSpanApiWithContext(subjectDateSpan, context), eventApi)) { + return false; + } + } + } + return true; + } + // Date Selection Validation + // ------------------------------------------------------------------------------------------------------------------------ + function isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig) { + var relevantEventStore = state.eventStore; + var relevantDefs = relevantEventStore.defs; + var relevantInstances = relevantEventStore.instances; + var selection = state.dateSelection; + var selectionRange = selection.range; + var selectionConfig = context.getCurrentData().selectionConfig; + if (filterConfig) { + selectionConfig = filterConfig(selectionConfig); + } + // constraint + if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, context)) { + return false; + } + // overlap + var selectOverlap = context.options.selectOverlap; + var selectOverlapFunc = typeof selectOverlap === 'function' ? selectOverlap : null; + for (var relevantInstanceId in relevantInstances) { + var relevantInstance = relevantInstances[relevantInstanceId]; + // intersect! evaluate + if (rangesIntersect(selectionRange, relevantInstance.range)) { + if (selectionConfig.overlap === false) { + return false; + } + if (selectOverlapFunc && !selectOverlapFunc(new EventApi(context, relevantDefs[relevantInstance.defId], relevantInstance), null)) { + return false; + } + } + } + // allow (a function) + for (var _i = 0, _a = selectionConfig.allows; _i < _a.length; _i++) { + var selectionAllow = _a[_i]; + var fullDateSpan = __assign(__assign({}, dateSpanMeta), selection); + if (!selectionAllow(buildDateSpanApiWithContext(fullDateSpan, context), null)) { + return false; + } + } + return true; + } + // Constraint Utils + // ------------------------------------------------------------------------------------------------------------------------ + function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, context) { + for (var _i = 0, constraints_1 = constraints; _i < constraints_1.length; _i++) { + var constraint = constraints_1[_i]; + if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, context), subjectRange)) { + return false; + } + } + return true; + } + function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours + otherEventStore, // for if constraint is an even group ID + businessHoursUnexpanded, // for if constraint is 'businessHours' + context // for expanding businesshours + ) { + if (constraint === 'businessHours') { + return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, context)); + } + else if (typeof constraint === 'string') { // an group ID + return eventStoreToRanges(filterEventStoreDefs(otherEventStore, function (eventDef) { + return eventDef.groupId === constraint; + })); + } + else if (typeof constraint === 'object' && constraint) { // non-null object + return eventStoreToRanges(expandRecurring(constraint, subjectRange, context)); + } + return []; // if it's false + } + // TODO: move to event-store file? + function eventStoreToRanges(eventStore) { + var instances = eventStore.instances; + var ranges = []; + for (var instanceId in instances) { + ranges.push(instances[instanceId].range); + } + return ranges; + } + // TODO: move to geom file? + function anyRangesContainRange(outerRanges, innerRange) { + for (var _i = 0, outerRanges_1 = outerRanges; _i < outerRanges_1.length; _i++) { + var outerRange = outerRanges_1[_i]; + if (rangeContainsRange(outerRange, innerRange)) { + return true; + } + } + return false; + } + + /* + an INTERACTABLE date component + + PURPOSES: + - hook up to fg, fill, and mirror renderers + - interface for dragging and hits + */ + var DateComponent = /** @class */ (function (_super) { + __extends(DateComponent, _super); + function DateComponent() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.uid = guid(); + return _this; + } + // Hit System + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.prepareHits = function () { + }; + DateComponent.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) { + return null; // this should be abstract + }; + // Validation + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.isInteractionValid = function (interaction) { + var dateProfile = this.props.dateProfile; // HACK + var instances = interaction.mutatedEvents.instances; + if (dateProfile) { // HACK for MorePopover + for (var instanceId in instances) { + if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) { + return false; + } + } + } + return isInteractionValid(interaction, this.context); + }; + DateComponent.prototype.isDateSelectionValid = function (selection) { + var dateProfile = this.props.dateProfile; // HACK + if (dateProfile && // HACK for MorePopover + !rangeContainsRange(dateProfile.validRange, selection.range)) { + return false; + } + return isDateSelectionValid(selection, this.context); + }; + // Pointer Interaction Utils + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.isValidSegDownEl = function (el) { + return !this.props.eventDrag && // HACK + !this.props.eventResize && // HACK + !elementClosest(el, '.fc-event-mirror') && + (this.isPopover() || !this.isInPopover(el)); + // ^above line ensures we don't detect a seg interaction within a nested component. + // it's a HACK because it only supports a popover as the nested component. + }; + DateComponent.prototype.isValidDateDownEl = function (el) { + return !elementClosest(el, '.fc-event:not(.fc-bg-event)') && + !elementClosest(el, '.fc-daygrid-more-link') && // a "more.." link + !elementClosest(el, 'a[data-navlink]') && // a clickable nav link + !this.isInPopover(el); + }; + DateComponent.prototype.isPopover = function () { + return false; + }; + DateComponent.prototype.isInPopover = function (el) { + return Boolean(elementClosest(el, '.fc-popover')); + }; + return DateComponent; + }(BaseComponent)); + + // TODO: easier way to add new hooks? need to update a million things + function createPlugin(input) { + return { + id: guid(), + deps: input.deps || [], + reducers: input.reducers || [], + contextInit: [].concat(input.contextInit || []), + eventRefiners: input.eventRefiners || {}, + eventDefMemberAdders: input.eventDefMemberAdders || [], + eventSourceRefiners: input.eventSourceRefiners || {}, + isDraggableTransformers: input.isDraggableTransformers || [], + eventDragMutationMassagers: input.eventDragMutationMassagers || [], + eventDefMutationAppliers: input.eventDefMutationAppliers || [], + dateSelectionTransformers: input.dateSelectionTransformers || [], + datePointTransforms: input.datePointTransforms || [], + dateSpanTransforms: input.dateSpanTransforms || [], + views: input.views || {}, + viewPropsTransformers: input.viewPropsTransformers || [], + isPropsValid: input.isPropsValid || null, + externalDefTransforms: input.externalDefTransforms || [], + eventResizeJoinTransforms: input.eventResizeJoinTransforms || [], + viewContainerAppends: input.viewContainerAppends || [], + eventDropTransformers: input.eventDropTransformers || [], + componentInteractions: input.componentInteractions || [], + calendarInteractions: input.calendarInteractions || [], + themeClasses: input.themeClasses || {}, + eventSourceDefs: input.eventSourceDefs || [], + cmdFormatter: input.cmdFormatter, + recurringTypes: input.recurringTypes || [], + namedTimeZonedImpl: input.namedTimeZonedImpl, + initialView: input.initialView || '', + elementDraggingImpl: input.elementDraggingImpl, + optionChangeHandlers: input.optionChangeHandlers || {}, + scrollGridImpl: input.scrollGridImpl || null, + contentTypeHandlers: input.contentTypeHandlers || {}, + listenerRefiners: input.listenerRefiners || {}, + optionRefiners: input.optionRefiners || {}, + propSetHandlers: input.propSetHandlers || {} + }; + } + function buildPluginHooks(pluginDefs, globalDefs) { + var isAdded = {}; + var hooks = { + reducers: [], + contextInit: [], + eventRefiners: {}, + eventDefMemberAdders: [], + eventSourceRefiners: {}, + isDraggableTransformers: [], + eventDragMutationMassagers: [], + eventDefMutationAppliers: [], + dateSelectionTransformers: [], + datePointTransforms: [], + dateSpanTransforms: [], + views: {}, + viewPropsTransformers: [], + isPropsValid: null, + externalDefTransforms: [], + eventResizeJoinTransforms: [], + viewContainerAppends: [], + eventDropTransformers: [], + componentInteractions: [], + calendarInteractions: [], + themeClasses: {}, + eventSourceDefs: [], + cmdFormatter: null, + recurringTypes: [], + namedTimeZonedImpl: null, + initialView: '', + elementDraggingImpl: null, + optionChangeHandlers: {}, + scrollGridImpl: null, + contentTypeHandlers: {}, + listenerRefiners: {}, + optionRefiners: {}, + propSetHandlers: {} + }; + function addDefs(defs) { + for (var _i = 0, defs_1 = defs; _i < defs_1.length; _i++) { + var def = defs_1[_i]; + if (!isAdded[def.id]) { + isAdded[def.id] = true; + addDefs(def.deps); + hooks = combineHooks(hooks, def); + } + } + } + if (pluginDefs) { + addDefs(pluginDefs); + } + addDefs(globalDefs); + return hooks; + } + function buildBuildPluginHooks() { + var currentOverrideDefs = []; + var currentGlobalDefs = []; + var currentHooks; + return function (overrideDefs, globalDefs) { + if (!currentHooks || !isArraysEqual(overrideDefs, currentOverrideDefs) || !isArraysEqual(globalDefs, currentGlobalDefs)) { + currentHooks = buildPluginHooks(overrideDefs, globalDefs); + } + currentOverrideDefs = overrideDefs; + currentGlobalDefs = globalDefs; + return currentHooks; + }; + } + function combineHooks(hooks0, hooks1) { + return { + reducers: hooks0.reducers.concat(hooks1.reducers), + contextInit: hooks0.contextInit.concat(hooks1.contextInit), + eventRefiners: __assign(__assign({}, hooks0.eventRefiners), hooks1.eventRefiners), + eventDefMemberAdders: hooks0.eventDefMemberAdders.concat(hooks1.eventDefMemberAdders), + eventSourceRefiners: __assign(__assign({}, hooks0.eventSourceRefiners), hooks1.eventSourceRefiners), + isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers), + eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers), + eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers), + dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers), + datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms), + dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms), + views: __assign(__assign({}, hooks0.views), hooks1.views), + viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers), + isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid, + externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms), + eventResizeJoinTransforms: hooks0.eventResizeJoinTransforms.concat(hooks1.eventResizeJoinTransforms), + viewContainerAppends: hooks0.viewContainerAppends.concat(hooks1.viewContainerAppends), + eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers), + calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions), + componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions), + themeClasses: __assign(__assign({}, hooks0.themeClasses), hooks1.themeClasses), + eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs), + cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter, + recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes), + namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl, + initialView: hooks0.initialView || hooks1.initialView, + elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl, + optionChangeHandlers: __assign(__assign({}, hooks0.optionChangeHandlers), hooks1.optionChangeHandlers), + scrollGridImpl: hooks1.scrollGridImpl || hooks0.scrollGridImpl, + contentTypeHandlers: __assign(__assign({}, hooks0.contentTypeHandlers), hooks1.contentTypeHandlers), + listenerRefiners: __assign(__assign({}, hooks0.listenerRefiners), hooks1.listenerRefiners), + optionRefiners: __assign(__assign({}, hooks0.optionRefiners), hooks1.optionRefiners), + propSetHandlers: __assign(__assign({}, hooks0.propSetHandlers), hooks1.propSetHandlers) + }; + } + + var StandardTheme = /** @class */ (function (_super) { + __extends(StandardTheme, _super); + function StandardTheme() { + return _super !== null && _super.apply(this, arguments) || this; + } + return StandardTheme; + }(Theme)); + StandardTheme.prototype.classes = { + root: 'fc-theme-standard', + tableCellShaded: 'fc-cell-shaded', + buttonGroup: 'fc-button-group', + button: 'fc-button fc-button-primary', + buttonActive: 'fc-button-active' + }; + StandardTheme.prototype.baseIconClass = 'fc-icon'; + StandardTheme.prototype.iconClasses = { + close: 'fc-icon-x', + prev: 'fc-icon-chevron-left', + next: 'fc-icon-chevron-right', + prevYear: 'fc-icon-chevrons-left', + nextYear: 'fc-icon-chevrons-right' + }; + StandardTheme.prototype.rtlIconClasses = { + prev: 'fc-icon-chevron-right', + next: 'fc-icon-chevron-left', + prevYear: 'fc-icon-chevrons-right', + nextYear: 'fc-icon-chevrons-left' + }; + StandardTheme.prototype.iconOverrideOption = 'buttonIcons'; // TODO: make TS-friendly + StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon'; + StandardTheme.prototype.iconOverridePrefix = 'fc-icon-'; + + function compileViewDefs(defaultConfigs, overrideConfigs) { + var hash = {}; + var viewType; + for (viewType in defaultConfigs) { + ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); + } + for (viewType in overrideConfigs) { + ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); + } + return hash; + } + function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) { + if (hash[viewType]) { + return hash[viewType]; + } + var viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs); + if (viewDef) { + hash[viewType] = viewDef; + } + return viewDef; + } + function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) { + var defaultConfig = defaultConfigs[viewType]; + var overrideConfig = overrideConfigs[viewType]; + var queryProp = function (name) { + return (defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] : + ((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null); + }; + var theComponent = queryProp('component'); + var superType = queryProp('superType'); + var superDef = null; + if (superType) { + if (superType === viewType) { + throw new Error('Can\'t have a custom view type that references itself'); + } + superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs); + } + if (!theComponent && superDef) { + theComponent = superDef.component; + } + if (!theComponent) { + return null; // don't throw a warning, might be settings for a single-unit view + } + return { + type: viewType, + component: theComponent, + defaults: __assign(__assign({}, (superDef ? superDef.defaults : {})), (defaultConfig ? defaultConfig.rawOptions : {})), + overrides: __assign(__assign({}, (superDef ? superDef.overrides : {})), (overrideConfig ? overrideConfig.rawOptions : {})) + }; + } + + // NOTE: in JSX, you should always use this class with arg. otherwise, will default to any??? + var RenderHook = /** @class */ (function (_super) { + __extends(RenderHook, _super); + function RenderHook() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.rootElRef = createRef(); + _this.handleRootEl = function (el) { + setRef(_this.rootElRef, el); + if (_this.props.elRef) { + setRef(_this.props.elRef, el); + } + }; + return _this; + } + RenderHook.prototype.render = function () { + var _this = this; + var props = this.props; + var hookProps = props.hookProps; + return (createElement(MountHook, { hookProps: hookProps, didMount: props.didMount, willUnmount: props.willUnmount, elRef: this.handleRootEl }, function (rootElRef) { return (createElement(ContentHook, { hookProps: hookProps, content: props.content, defaultContent: props.defaultContent, backupElRef: _this.rootElRef }, function (innerElRef, innerContent) { return props.children(rootElRef, normalizeClassNames(props.classNames, hookProps), innerElRef, innerContent); })); })); + }; + return RenderHook; + }(BaseComponent)); + // for forcing rerender of components that use the ContentHook + var CustomContentRenderContext = createContext$1(0); + function ContentHook(props) { + return (createElement(CustomContentRenderContext.Consumer, null, function (renderId) { return (createElement(ContentHookInner, __assign({ renderId: renderId }, props))); })); + } + var ContentHookInner = /** @class */ (function (_super) { + __extends(ContentHookInner, _super); + function ContentHookInner() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.innerElRef = createRef(); + return _this; + } + ContentHookInner.prototype.render = function () { + return this.props.children(this.innerElRef, this.renderInnerContent()); + }; + ContentHookInner.prototype.componentDidMount = function () { + this.updateCustomContent(); + }; + ContentHookInner.prototype.componentDidUpdate = function () { + this.updateCustomContent(); + }; + ContentHookInner.prototype.renderInnerContent = function () { + var contentTypeHandlers = this.context.pluginHooks.contentTypeHandlers; + var _a = this, props = _a.props, customContentInfo = _a.customContentInfo; + var rawVal = props.content; + var innerContent = normalizeContent(rawVal, props.hookProps); + var innerContentVDom = null; + if (innerContent === undefined) { // use the default + innerContent = normalizeContent(props.defaultContent, props.hookProps); + } + if (innerContent !== undefined) { // we allow custom content handlers to return nothing + if (customContentInfo) { + customContentInfo.contentVal = innerContent[customContentInfo.contentKey]; + } + else if (typeof innerContent === 'object') { + // look for a prop that would indicate a custom content handler is needed + for (var contentKey in contentTypeHandlers) { + if (innerContent[contentKey] !== undefined) { + customContentInfo = this.customContentInfo = { + contentKey: contentKey, + contentVal: innerContent[contentKey], + handler: contentTypeHandlers[contentKey]() + }; + break; + } + } + } + if (customContentInfo) { + innerContentVDom = []; // signal that something was specified + } + else { + innerContentVDom = innerContent; // assume a [p]react vdom node. use it + } + } + return innerContentVDom; + }; + ContentHookInner.prototype.updateCustomContent = function () { + if (this.customContentInfo) { + this.customContentInfo.handler(this.innerElRef.current || this.props.backupElRef.current, // the element to render into + this.customContentInfo.contentVal); + } + }; + return ContentHookInner; + }(BaseComponent)); + var MountHook = /** @class */ (function (_super) { + __extends(MountHook, _super); + function MountHook() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.handleRootEl = function (rootEl) { + _this.rootEl = rootEl; + if (_this.props.elRef) { + setRef(_this.props.elRef, rootEl); + } + }; + return _this; + } + MountHook.prototype.render = function () { + return this.props.children(this.handleRootEl); + }; + MountHook.prototype.componentDidMount = function () { + var callback = this.props.didMount; + callback && callback(__assign(__assign({}, this.props.hookProps), { el: this.rootEl })); + }; + MountHook.prototype.componentWillUnmount = function () { + var callback = this.props.willUnmount; + callback && callback(__assign(__assign({}, this.props.hookProps), { el: this.rootEl })); + }; + return MountHook; + }(BaseComponent)); + function buildClassNameNormalizer() { + var currentGenerator; + var currentHookProps; + var currentClassNames = []; + return function (generator, hookProps) { + if (!currentHookProps || !isPropsEqual(currentHookProps, hookProps) || generator !== currentGenerator) { + currentGenerator = generator; + currentHookProps = hookProps; + currentClassNames = normalizeClassNames(generator, hookProps); + } + return currentClassNames; + }; + } + function normalizeClassNames(classNames, hookProps) { + if (typeof classNames === 'function') { + classNames = classNames(hookProps); + } + return parseClassNames(classNames); + } + function normalizeContent(input, hookProps) { + if (typeof input === 'function') { + return input(hookProps, createElement); // give the function the vdom-creation func + } + else { + return input; + } + } + + var ViewRoot = /** @class */ (function (_super) { + __extends(ViewRoot, _super); + function ViewRoot() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.normalizeClassNames = buildClassNameNormalizer(); + return _this; + } + ViewRoot.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var options = context.options; + var hookProps = { view: context.viewApi }; + var customClassNames = this.normalizeClassNames(options.viewClassNames, hookProps); + return (createElement(MountHook, { hookProps: hookProps, didMount: options.viewDidMount, willUnmount: options.viewWillUnmount, elRef: props.elRef }, function (rootElRef) { return props.children(rootElRef, ["fc-" + props.viewSpec.type + "-view", 'fc-view'].concat(customClassNames)); })); + }; + return ViewRoot; + }(BaseComponent)); + + function parseViewConfigs(inputs) { + return mapHash(inputs, parseViewConfig); + } + function parseViewConfig(input) { + var rawOptions = typeof input === 'function' ? + { component: input } : + input; + var component = rawOptions.component; + if (rawOptions.content) { + component = createViewHookComponent(rawOptions); + // TODO: remove content/classNames/didMount/etc from options? + } + return { + superType: rawOptions.type, + component: component, + rawOptions: rawOptions // includes type and component too :( + }; + } + function createViewHookComponent(options) { + return function (viewProps) { + return (createElement(ViewContextType.Consumer, null, function (context) { return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, viewClassNames) { + var hookProps = __assign(__assign({}, viewProps), { nextDayThreshold: context.options.nextDayThreshold }); + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.classNames, content: options.content, didMount: options.didMount, willUnmount: options.willUnmount, elRef: rootElRef }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("div", { className: viewClassNames.concat(customClassNames).join(' '), ref: rootElRef }, innerContent)); })); + })); })); + }; + } + + function buildViewSpecs(defaultInputs, optionOverrides, dynamicOptionOverrides, localeDefaults) { + var defaultConfigs = parseViewConfigs(defaultInputs); + var overrideConfigs = parseViewConfigs(optionOverrides.views); + var viewDefs = compileViewDefs(defaultConfigs, overrideConfigs); + return mapHash(viewDefs, function (viewDef) { + return buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults); + }); + } + function buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults) { + var durationInput = viewDef.overrides.duration || + viewDef.defaults.duration || + dynamicOptionOverrides.duration || + optionOverrides.duration; + var duration = null; + var durationUnit = ''; + var singleUnit = ''; + var singleUnitOverrides = {}; + if (durationInput) { + duration = createDurationCached(durationInput); + if (duration) { // valid? + var denom = greatestDurationDenominator(duration); + durationUnit = denom.unit; + if (denom.value === 1) { + singleUnit = durationUnit; + singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].rawOptions : {}; + } + } + } + var queryButtonText = function (optionsSubset) { + var buttonTextMap = optionsSubset.buttonText || {}; + var buttonTextKey = viewDef.defaults.buttonTextKey; + if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) { + return buttonTextMap[buttonTextKey]; + } + if (buttonTextMap[viewDef.type] != null) { + return buttonTextMap[viewDef.type]; + } + if (buttonTextMap[singleUnit] != null) { + return buttonTextMap[singleUnit]; + } + }; + return { + type: viewDef.type, + component: viewDef.component, + duration: duration, + durationUnit: durationUnit, + singleUnit: singleUnit, + optionDefaults: viewDef.defaults, + optionOverrides: __assign(__assign({}, singleUnitOverrides), viewDef.overrides), + buttonTextOverride: queryButtonText(dynamicOptionOverrides) || + queryButtonText(optionOverrides) || // constructor-specified buttonText lookup hash takes precedence + viewDef.overrides.buttonText, + buttonTextDefault: queryButtonText(localeDefaults) || + viewDef.defaults.buttonText || + queryButtonText(BASE_OPTION_DEFAULTS) || + viewDef.type // fall back to given view name + }; + } + // hack to get memoization working + var durationInputMap = {}; + function createDurationCached(durationInput) { + var json = JSON.stringify(durationInput); + var res = durationInputMap[json]; + if (res === undefined) { + res = createDuration(durationInput); + durationInputMap[json] = res; + } + return res; + } + + var DateProfileGenerator = /** @class */ (function () { + function DateProfileGenerator(props) { + this.props = props; + this.nowDate = getNow(props.nowInput, props.dateEnv); + this.initHiddenDays(); + } + /* Date Range Computation + ------------------------------------------------------------------------------------------------------------------*/ + // Builds a structure with info about what the dates/ranges will be for the "prev" view. + DateProfileGenerator.prototype.buildPrev = function (currentDateProfile, currentDate, forceToValid) { + var dateEnv = this.props.dateEnv; + var prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month + currentDateProfile.dateIncrement); + return this.build(prevDate, -1, forceToValid); + }; + // Builds a structure with info about what the dates/ranges will be for the "next" view. + DateProfileGenerator.prototype.buildNext = function (currentDateProfile, currentDate, forceToValid) { + var dateEnv = this.props.dateEnv; + var nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month + currentDateProfile.dateIncrement); + return this.build(nextDate, 1, forceToValid); + }; + // Builds a structure holding dates/ranges for rendering around the given date. + // Optional direction param indicates whether the date is being incremented/decremented + // from its previous value. decremented = -1, incremented = 1 (default). + DateProfileGenerator.prototype.build = function (currentDate, direction, forceToValid) { + if (forceToValid === void 0) { forceToValid = true; } + var props = this.props; + var validRange; + var currentInfo; + var isRangeAllDay; + var renderRange; + var activeRange; + var isValid; + validRange = this.buildValidRange(); + validRange = this.trimHiddenDays(validRange); + if (forceToValid) { + currentDate = constrainMarkerToRange(currentDate, validRange); + } + currentInfo = this.buildCurrentRangeInfo(currentDate, direction); + isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit); + renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay); + renderRange = this.trimHiddenDays(renderRange); + activeRange = renderRange; + if (!props.showNonCurrentDates) { + activeRange = intersectRanges(activeRange, currentInfo.range); + } + activeRange = this.adjustActiveRange(activeRange); + activeRange = intersectRanges(activeRange, validRange); // might return null + // it's invalid if the originally requested date is not contained, + // or if the range is completely outside of the valid range. + isValid = rangesIntersect(currentInfo.range, validRange); + return { + // constraint for where prev/next operations can go and where events can be dragged/resized to. + // an object with optional start and end properties. + validRange: validRange, + // range the view is formally responsible for. + // for example, a month view might have 1st-31st, excluding padded dates + currentRange: currentInfo.range, + // name of largest unit being displayed, like "month" or "week" + currentRangeUnit: currentInfo.unit, + isRangeAllDay: isRangeAllDay, + // dates that display events and accept drag-n-drop + // will be `null` if no dates accept events + activeRange: activeRange, + // date range with a rendered skeleton + // includes not-active days that need some sort of DOM + renderRange: renderRange, + // Duration object that denotes the first visible time of any given day + slotMinTime: props.slotMinTime, + // Duration object that denotes the exclusive visible end time of any given day + slotMaxTime: props.slotMaxTime, + isValid: isValid, + // how far the current date will move for a prev/next operation + dateIncrement: this.buildDateIncrement(currentInfo.duration) + // pass a fallback (might be null) ^ + }; + }; + // Builds an object with optional start/end properties. + // Indicates the minimum/maximum dates to display. + // not responsible for trimming hidden days. + DateProfileGenerator.prototype.buildValidRange = function () { + var input = this.props.validRangeInput; + var simpleInput = typeof input === 'function' + ? input.call(this.props.calendarApi, this.nowDate) + : input; + return this.refineRange(simpleInput) || + { start: null, end: null }; // completely open-ended + }; + // Builds a structure with info about the "current" range, the range that is + // highlighted as being the current month for example. + // See build() for a description of `direction`. + // Guaranteed to have `range` and `unit` properties. `duration` is optional. + DateProfileGenerator.prototype.buildCurrentRangeInfo = function (date, direction) { + var props = this.props; + var duration = null; + var unit = null; + var range = null; + var dayCount; + if (props.duration) { + duration = props.duration; + unit = props.durationUnit; + range = this.buildRangeFromDuration(date, direction, duration, unit); + } + else if ((dayCount = this.props.dayCount)) { + unit = 'day'; + range = this.buildRangeFromDayCount(date, direction, dayCount); + } + else if ((range = this.buildCustomVisibleRange(date))) { + unit = props.dateEnv.greatestWholeUnit(range.start, range.end).unit; + } + else { + duration = this.getFallbackDuration(); + unit = greatestDurationDenominator(duration).unit; + range = this.buildRangeFromDuration(date, direction, duration, unit); + } + return { duration: duration, unit: unit, range: range }; + }; + DateProfileGenerator.prototype.getFallbackDuration = function () { + return createDuration({ day: 1 }); + }; + // Returns a new activeRange to have time values (un-ambiguate) + // slotMinTime or slotMaxTime causes the range to expand. + DateProfileGenerator.prototype.adjustActiveRange = function (range) { + var _a = this.props, dateEnv = _a.dateEnv, usesMinMaxTime = _a.usesMinMaxTime, slotMinTime = _a.slotMinTime, slotMaxTime = _a.slotMaxTime; + var start = range.start; + var end = range.end; + if (usesMinMaxTime) { + // expand active range if slotMinTime is negative (why not when positive?) + if (asRoughDays(slotMinTime) < 0) { + start = startOfDay(start); // necessary? + start = dateEnv.add(start, slotMinTime); + } + // expand active range if slotMaxTime is beyond one day (why not when negative?) + if (asRoughDays(slotMaxTime) > 1) { + end = startOfDay(end); // necessary? + end = addDays(end, -1); + end = dateEnv.add(end, slotMaxTime); + } + } + return { start: start, end: end }; + }; + // Builds the "current" range when it is specified as an explicit duration. + // `unit` is the already-computed greatestDurationDenominator unit of duration. + DateProfileGenerator.prototype.buildRangeFromDuration = function (date, direction, duration, unit) { + var _a = this.props, dateEnv = _a.dateEnv, dateAlignment = _a.dateAlignment; + var start; + var end; + var res; + // compute what the alignment should be + if (!dateAlignment) { + var dateIncrement = this.props.dateIncrement; + if (dateIncrement) { + // use the smaller of the two units + if (asRoughMs(dateIncrement) < asRoughMs(duration)) { + dateAlignment = greatestDurationDenominator(dateIncrement).unit; + } + else { + dateAlignment = unit; + } + } + else { + dateAlignment = unit; + } + } + // if the view displays a single day or smaller + if (asRoughDays(duration) <= 1) { + if (this.isHiddenDay(start)) { + start = this.skipHiddenDays(start, direction); + start = startOfDay(start); + } + } + function computeRes() { + start = dateEnv.startOf(date, dateAlignment); + end = dateEnv.add(start, duration); + res = { start: start, end: end }; + } + computeRes(); + // if range is completely enveloped by hidden days, go past the hidden days + if (!this.trimHiddenDays(res)) { + date = this.skipHiddenDays(date, direction); + computeRes(); + } + return res; + }; + // Builds the "current" range when a dayCount is specified. + DateProfileGenerator.prototype.buildRangeFromDayCount = function (date, direction, dayCount) { + var _a = this.props, dateEnv = _a.dateEnv, dateAlignment = _a.dateAlignment; + var runningCount = 0; + var start = date; + var end; + if (dateAlignment) { + start = dateEnv.startOf(start, dateAlignment); + } + start = startOfDay(start); + start = this.skipHiddenDays(start, direction); + end = start; + do { + end = addDays(end, 1); + if (!this.isHiddenDay(end)) { + runningCount++; + } + } while (runningCount < dayCount); + return { start: start, end: end }; + }; + // Builds a normalized range object for the "visible" range, + // which is a way to define the currentRange and activeRange at the same time. + DateProfileGenerator.prototype.buildCustomVisibleRange = function (date) { + var props = this.props; + var input = props.visibleRangeInput; + var simpleInput = typeof input === 'function' + ? input.call(props.calendarApi, props.dateEnv.toDate(date)) + : input; + var range = this.refineRange(simpleInput); + if (range && (range.start == null || range.end == null)) { + return null; + } + return range; + }; + // Computes the range that will represent the element/cells for *rendering*, + // but which may have voided days/times. + // not responsible for trimming hidden days. + DateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { + return currentRange; + }; + // Compute the duration value that should be added/substracted to the current date + // when a prev/next operation happens. + DateProfileGenerator.prototype.buildDateIncrement = function (fallback) { + var dateIncrement = this.props.dateIncrement; + var customAlignment; + if (dateIncrement) { + return dateIncrement; + } + else if ((customAlignment = this.props.dateAlignment)) { + return createDuration(1, customAlignment); + } + else if (fallback) { + return fallback; + } + else { + return createDuration({ days: 1 }); + } + }; + DateProfileGenerator.prototype.refineRange = function (rangeInput) { + if (rangeInput) { + var range = parseRange(rangeInput, this.props.dateEnv); + if (range) { + range = computeVisibleDayRange(range); + } + return range; + } + return null; + }; + /* Hidden Days + ------------------------------------------------------------------------------------------------------------------*/ + // Initializes internal variables related to calculating hidden days-of-week + DateProfileGenerator.prototype.initHiddenDays = function () { + var hiddenDays = this.props.hiddenDays || []; // array of day-of-week indices that are hidden + var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool) + var dayCnt = 0; + var i; + if (this.props.weekends === false) { + hiddenDays.push(0, 6); // 0=sunday, 6=saturday + } + for (i = 0; i < 7; i++) { + if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) { + dayCnt++; + } + } + if (!dayCnt) { + throw new Error('invalid hiddenDays'); // all days were hidden? bad. + } + this.isHiddenDayHash = isHiddenDayHash; + }; + // Remove days from the beginning and end of the range that are computed as hidden. + // If the whole range is trimmed off, returns null + DateProfileGenerator.prototype.trimHiddenDays = function (range) { + var start = range.start; + var end = range.end; + if (start) { + start = this.skipHiddenDays(start); + } + if (end) { + end = this.skipHiddenDays(end, -1, true); + } + if (start == null || end == null || start < end) { + return { start: start, end: end }; + } + return null; + }; + // Is the current day hidden? + // `day` is a day-of-week index (0-6), or a Date (used for UTC) + DateProfileGenerator.prototype.isHiddenDay = function (day) { + if (day instanceof Date) { + day = day.getUTCDay(); + } + return this.isHiddenDayHash[day]; + }; + // Incrementing the current day until it is no longer a hidden day, returning a copy. + // DOES NOT CONSIDER validRange! + // If the initial value of `date` is not a hidden day, don't do anything. + // Pass `isExclusive` as `true` if you are dealing with an end date. + // `inc` defaults to `1` (increment one day forward each time) + DateProfileGenerator.prototype.skipHiddenDays = function (date, inc, isExclusive) { + if (inc === void 0) { inc = 1; } + if (isExclusive === void 0) { isExclusive = false; } + while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) { + date = addDays(date, inc); + } + return date; + }; + return DateProfileGenerator; + }()); + + function reduceViewType(viewType, action) { + switch (action.type) { + case 'CHANGE_VIEW_TYPE': + return viewType = action.viewType; + } + return viewType; + } + + function reduceDynamicOptionOverrides(dynamicOptionOverrides, action) { + var _a; + switch (action.type) { + case 'SET_OPTION': + return __assign(__assign({}, dynamicOptionOverrides), (_a = {}, _a[action.optionName] = action.rawOptionValue, _a)); + default: + return dynamicOptionOverrides; + } + } + + function reduceDateProfile(currentDateProfile, action, currentDate, dateProfileGenerator) { + var dp; + switch (action.type) { + case 'CHANGE_VIEW_TYPE': + return dateProfileGenerator.build(action.dateMarker || currentDate); + case 'CHANGE_DATE': + if (!currentDateProfile.activeRange || + !rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker) // don't move if date already in view + ) { + return dateProfileGenerator.build(action.dateMarker); + } + break; + case 'PREV': + dp = dateProfileGenerator.buildPrev(currentDateProfile, currentDate); + if (dp.isValid) { + return dp; + } + break; + case 'NEXT': + dp = dateProfileGenerator.buildNext(currentDateProfile, currentDate); + if (dp.isValid) { + return dp; + } + break; + } + return currentDateProfile; + } + + function initEventSources(calendarOptions, dateProfile, context) { + var activeRange = dateProfile ? dateProfile.activeRange : null; + return addSources({}, parseInitialSources(calendarOptions, context), activeRange, context); + } + function reduceEventSources(eventSources, action, dateProfile, context) { + var activeRange = dateProfile ? dateProfile.activeRange : null; // need this check? + switch (action.type) { + case 'ADD_EVENT_SOURCES': // already parsed + return addSources(eventSources, action.sources, activeRange, context); + case 'REMOVE_EVENT_SOURCE': + return removeSource(eventSources, action.sourceId); + case 'PREV': // TODO: how do we track all actions that affect dateProfile :( + case 'NEXT': + case 'CHANGE_DATE': + case 'CHANGE_VIEW_TYPE': + if (dateProfile) { + return fetchDirtySources(eventSources, activeRange, context); + } + else { + return eventSources; + } + case 'FETCH_EVENT_SOURCES': + return fetchSourcesByIds(eventSources, action.sourceIds ? // why no type? + arrayToHash(action.sourceIds) : + excludeStaticSources(eventSources, context), activeRange, context); + case 'RECEIVE_EVENTS': + case 'RECEIVE_EVENT_ERROR': + return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange); + case 'REMOVE_ALL_EVENT_SOURCES': + return {}; + default: + return eventSources; + } + } + function reduceEventSourcesNewTimeZone(eventSources, dateProfile, context) { + var activeRange = dateProfile ? dateProfile.activeRange : null; // need this check? + return fetchSourcesByIds(eventSources, excludeStaticSources(eventSources, context), activeRange, context); + } + function computeEventSourceLoadingLevel(eventSources) { + var cnt = 0; + for (var sourceId in eventSources) { + if (eventSources[sourceId].isFetching) { + cnt++; + } + } + return cnt; + } + function addSources(eventSourceHash, sources, fetchRange, context) { + var hash = {}; + for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) { + var source = sources_1[_i]; + hash[source.sourceId] = source; + } + if (fetchRange) { + hash = fetchDirtySources(hash, fetchRange, context); + } + return __assign(__assign({}, eventSourceHash), hash); + } + function removeSource(eventSourceHash, sourceId) { + return filterHash(eventSourceHash, function (eventSource) { + return eventSource.sourceId !== sourceId; + }); + } + function fetchDirtySources(sourceHash, fetchRange, context) { + return fetchSourcesByIds(sourceHash, filterHash(sourceHash, function (eventSource) { + return isSourceDirty(eventSource, fetchRange, context); + }), fetchRange, context); + } + function isSourceDirty(eventSource, fetchRange, context) { + if (!doesSourceNeedRange(eventSource, context)) { + return !eventSource.latestFetchId; + } + else { + return !context.options.lazyFetching || + !eventSource.fetchRange || + eventSource.isFetching || // always cancel outdated in-progress fetches + fetchRange.start < eventSource.fetchRange.start || + fetchRange.end > eventSource.fetchRange.end; + } + } + function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, context) { + var nextSources = {}; + for (var sourceId in prevSources) { + var source = prevSources[sourceId]; + if (sourceIdHash[sourceId]) { + nextSources[sourceId] = fetchSource(source, fetchRange, context); + } + else { + nextSources[sourceId] = source; + } + } + return nextSources; + } + function fetchSource(eventSource, fetchRange, context) { + var options = context.options, calendarApi = context.calendarApi; + var sourceDef = context.pluginHooks.eventSourceDefs[eventSource.sourceDefId]; + var fetchId = guid(); + sourceDef.fetch({ + eventSource: eventSource, + range: fetchRange, + context: context + }, function (res) { + var rawEvents = res.rawEvents; + if (options.eventSourceSuccess) { + rawEvents = options.eventSourceSuccess.call(calendarApi, rawEvents, res.xhr) || rawEvents; + } + if (eventSource.success) { + rawEvents = eventSource.success.call(calendarApi, rawEvents, res.xhr) || rawEvents; + } + context.dispatch({ + type: 'RECEIVE_EVENTS', + sourceId: eventSource.sourceId, + fetchId: fetchId, + fetchRange: fetchRange, + rawEvents: rawEvents + }); + }, function (error) { + console.warn(error.message, error); + if (options.eventSourceFailure) { + options.eventSourceFailure.call(calendarApi, error); + } + if (eventSource.failure) { + eventSource.failure(error); + } + context.dispatch({ + type: 'RECEIVE_EVENT_ERROR', + sourceId: eventSource.sourceId, + fetchId: fetchId, + fetchRange: fetchRange, + error: error + }); + }); + return __assign(__assign({}, eventSource), { isFetching: true, latestFetchId: fetchId }); + } + function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) { + var _a; + var eventSource = sourceHash[sourceId]; + if (eventSource && // not already removed + fetchId === eventSource.latestFetchId) { + return __assign(__assign({}, sourceHash), (_a = {}, _a[sourceId] = __assign(__assign({}, eventSource), { isFetching: false, fetchRange: fetchRange // also serves as a marker that at least one fetch has completed + }), _a)); + } + return sourceHash; + } + function excludeStaticSources(eventSources, context) { + return filterHash(eventSources, function (eventSource) { + return doesSourceNeedRange(eventSource, context); + }); + } + function parseInitialSources(rawOptions, context) { + var refiners = buildEventSourceRefiners(context); + var rawSources = [].concat(rawOptions.eventSources || []); + var sources = []; // parsed + if (rawOptions.initialEvents) { + rawSources.unshift(rawOptions.initialEvents); + } + if (rawOptions.events) { + rawSources.unshift(rawOptions.events); + } + for (var _i = 0, rawSources_1 = rawSources; _i < rawSources_1.length; _i++) { + var rawSource = rawSources_1[_i]; + var source = parseEventSource(rawSource, context, refiners); + if (source) { + sources.push(source); + } + } + return sources; + } + function doesSourceNeedRange(eventSource, context) { + var defs = context.pluginHooks.eventSourceDefs; + return !defs[eventSource.sourceDefId].ignoreRange; + } + + function reduceDateSelection(currentSelection, action) { + switch (action.type) { + case 'UNSELECT_DATES': + return null; + case 'SELECT_DATES': + return action.selection; + default: + return currentSelection; + } + } + + function reduceSelectedEvent(currentInstanceId, action) { + switch (action.type) { + case 'UNSELECT_EVENT': + return ''; + case 'SELECT_EVENT': + return action.eventInstanceId; + default: + return currentInstanceId; + } + } + + function reduceEventDrag(currentDrag, action) { + var newDrag; + switch (action.type) { + case 'UNSET_EVENT_DRAG': + return null; + case 'SET_EVENT_DRAG': + newDrag = action.state; + return { + affectedEvents: newDrag.affectedEvents, + mutatedEvents: newDrag.mutatedEvents, + isEvent: newDrag.isEvent + }; + default: + return currentDrag; + } + } + + function reduceEventResize(currentResize, action) { + var newResize; + switch (action.type) { + case 'UNSET_EVENT_RESIZE': + return null; + case 'SET_EVENT_RESIZE': + newResize = action.state; + return { + affectedEvents: newResize.affectedEvents, + mutatedEvents: newResize.mutatedEvents, + isEvent: newResize.isEvent + }; + default: + return currentResize; + } + } + + function parseToolbars(calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) { + var viewsWithButtons = []; + var headerToolbar = calendarOptions.headerToolbar ? parseToolbar(calendarOptions.headerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) : null; + var footerToolbar = calendarOptions.footerToolbar ? parseToolbar(calendarOptions.footerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) : null; + return { headerToolbar: headerToolbar, footerToolbar: footerToolbar, viewsWithButtons: viewsWithButtons }; + } + function parseToolbar(sectionStrHash, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons // dump side effects + ) { + return mapHash(sectionStrHash, function (sectionStr) { return parseSection(sectionStr, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons); }); + } + /* + BAD: querying icons and text here. should be done at render time + */ + function parseSection(sectionStr, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons // dump side effects + ) { + var isRtl = calendarOptions.direction === 'rtl'; + var calendarCustomButtons = calendarOptions.customButtons || {}; + var calendarButtonTextOverrides = calendarOptionOverrides.buttonText || {}; + var calendarButtonText = calendarOptions.buttonText || {}; + var sectionSubstrs = sectionStr ? sectionStr.split(' ') : []; + return sectionSubstrs.map(function (buttonGroupStr) { + return buttonGroupStr.split(',').map(function (buttonName) { + if (buttonName === 'title') { + return { buttonName: buttonName }; + } + else { + var customButtonProps_1; + var viewSpec = void 0; + var buttonClick = void 0; + var buttonIcon = void 0; // only one of these will be set + var buttonText = void 0; // " + if ((customButtonProps_1 = calendarCustomButtons[buttonName])) { + buttonClick = function (ev) { + if (customButtonProps_1.click) { + customButtonProps_1.click.call(ev.target, ev, ev.target); + } + }; + (buttonIcon = theme.getCustomButtonIconClass(customButtonProps_1)) || + (buttonIcon = theme.getIconClass(buttonName, isRtl)) || + (buttonText = customButtonProps_1.text); + } + else if ((viewSpec = viewSpecs[buttonName])) { + viewsWithButtons.push(buttonName); + buttonClick = function () { + calendarApi.changeView(buttonName); + }; + (buttonText = viewSpec.buttonTextOverride) || + (buttonIcon = theme.getIconClass(buttonName, isRtl)) || + (buttonText = viewSpec.buttonTextDefault); + } + else if (calendarApi[buttonName]) { // a calendarApi method + buttonClick = function () { + calendarApi[buttonName](); + }; + (buttonText = calendarButtonTextOverrides[buttonName]) || + (buttonIcon = theme.getIconClass(buttonName, isRtl)) || + (buttonText = calendarButtonText[buttonName]); + // ^ everything else is considered default + } + return { buttonName: buttonName, buttonClick: buttonClick, buttonIcon: buttonIcon, buttonText: buttonText }; + } + }); + }); + } + + var eventSourceDef = { + ignoreRange: true, + parseMeta: function (refined) { + if (Array.isArray(refined.events)) { + return refined.events; + } + return null; + }, + fetch: function (arg, success) { + success({ + rawEvents: arg.eventSource.meta + }); + } + }; + var arrayEventSourcePlugin = createPlugin({ + eventSourceDefs: [eventSourceDef] + }); + + var eventSourceDef$1 = { + parseMeta: function (refined) { + if (typeof refined.events === 'function') { + return refined.events; + } + return null; + }, + fetch: function (arg, success, failure) { + var dateEnv = arg.context.dateEnv; + var func = arg.eventSource.meta; + unpromisify(func.bind(null, buildRangeApiWithTimeZone(arg.range, dateEnv)), function (rawEvents) { + success({ rawEvents: rawEvents }); // needs an object response + }, failure // send errorObj directly to failure callback + ); + } + }; + var funcEventSourcePlugin = createPlugin({ + eventSourceDefs: [eventSourceDef$1] + }); + + function requestJson(method, url, params, successCallback, failureCallback) { + method = method.toUpperCase(); + var body = null; + if (method === 'GET') { + url = injectQueryStringParams(url, params); + } + else { + body = encodeParams(params); + } + var xhr = new XMLHttpRequest(); + xhr.open(method, url, true); + if (method !== 'GET') { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + xhr.onload = function () { + if (xhr.status >= 200 && xhr.status < 400) { + var parsed = false; + var res = void 0; + try { + res = JSON.parse(xhr.responseText); + parsed = true; + } + catch (err) { + // will handle parsed=false + } + if (parsed) { + successCallback(res, xhr); + } + else { + failureCallback('Failure parsing JSON', xhr); + } + } + else { + failureCallback('Request failed', xhr); + } + }; + xhr.onerror = function () { + failureCallback('Request failed', xhr); + }; + xhr.send(body); + } + function injectQueryStringParams(url, params) { + return url + + (url.indexOf('?') === -1 ? '?' : '&') + + encodeParams(params); + } + function encodeParams(params) { + var parts = []; + for (var key in params) { + parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); + } + return parts.join('&'); + } + + var JSON_FEED_EVENT_SOURCE_REFINERS = { + method: String, + extraParams: identity, + startParam: String, + endParam: String, + timeZoneParam: String + }; + + var eventSourceDef$2 = { + parseMeta: function (refined) { + if (refined.url) { + return { + url: refined.url, + method: (refined.method || 'GET').toUpperCase(), + extraParams: refined.extraParams, + startParam: refined.startParam, + endParam: refined.endParam, + timeZoneParam: refined.timeZoneParam + }; + } + return null; + }, + fetch: function (arg, success, failure) { + var meta = arg.eventSource.meta; + var requestParams = buildRequestParams(meta, arg.range, arg.context); + requestJson(meta.method, meta.url, requestParams, function (rawEvents, xhr) { + success({ rawEvents: rawEvents, xhr: xhr }); + }, function (errorMessage, xhr) { + failure({ message: errorMessage, xhr: xhr }); + }); + } + }; + var jsonFeedEventSourcePlugin = createPlugin({ + eventSourceRefiners: JSON_FEED_EVENT_SOURCE_REFINERS, + eventSourceDefs: [eventSourceDef$2] + }); + function buildRequestParams(meta, range, context) { + var dateEnv = context.dateEnv, options = context.options; + var startParam; + var endParam; + var timeZoneParam; + var customRequestParams; + var params = {}; + startParam = meta.startParam; + if (startParam == null) { + startParam = options.startParam; + } + endParam = meta.endParam; + if (endParam == null) { + endParam = options.endParam; + } + timeZoneParam = meta.timeZoneParam; + if (timeZoneParam == null) { + timeZoneParam = options.timeZoneParam; + } + // retrieve any outbound GET/POST data from the options + if (typeof meta.extraParams === 'function') { + // supplied as a function that returns a key/value object + customRequestParams = meta.extraParams(); + } + else { + // probably supplied as a straight key/value object + customRequestParams = meta.extraParams || {}; + } + __assign(params, customRequestParams); + params[startParam] = dateEnv.formatIso(range.start); + params[endParam] = dateEnv.formatIso(range.end); + if (dateEnv.timeZone !== 'local') { + params[timeZoneParam] = dateEnv.timeZone; + } + return params; + } + + var SIMPLE_RECURRING_REFINERS = { + daysOfWeek: identity, + startTime: createDuration, + endTime: createDuration, + duration: createDuration, + startRecur: identity, + endRecur: identity + }; + + var recurring = { + parse: function (refined, dateEnv) { + if (refined.daysOfWeek || refined.startTime || refined.endTime || refined.startRecur || refined.endRecur) { + var recurringData = { + daysOfWeek: refined.daysOfWeek || null, + startTime: refined.startTime || null, + endTime: refined.endTime || null, + startRecur: refined.startRecur ? dateEnv.createMarker(refined.startRecur) : null, + endRecur: refined.endRecur ? dateEnv.createMarker(refined.endRecur) : null + }; + var duration = void 0; + if (refined.duration) { + duration = refined.duration; + } + if (!duration && refined.startTime && refined.endTime) { + duration = subtractDurations(refined.endTime, refined.startTime); + } + return { + allDayGuess: Boolean(!refined.startTime && !refined.endTime), + duration: duration, + typeData: recurringData // doesn't need endTime anymore but oh well + }; + } + return null; + }, + expand: function (typeData, framingRange, dateEnv) { + var clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur }); + if (clippedFramingRange) { + return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv); + } + else { + return []; + } + } + }; + var simpleRecurringEventsPlugin = createPlugin({ + recurringTypes: [recurring], + eventRefiners: SIMPLE_RECURRING_REFINERS + }); + function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) { + var dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null; + var dayMarker = startOfDay(framingRange.start); + var endMarker = framingRange.end; + var instanceStarts = []; + while (dayMarker < endMarker) { + var instanceStart + // if everyday, or this particular day-of-week + = void 0; + // if everyday, or this particular day-of-week + if (!dowHash || dowHash[dayMarker.getUTCDay()]) { + if (startTime) { + instanceStart = dateEnv.add(dayMarker, startTime); + } + else { + instanceStart = dayMarker; + } + instanceStarts.push(instanceStart); + } + dayMarker = addDays(dayMarker, 1); + } + return instanceStarts; + } + + var changeHandlerPlugin = createPlugin({ + optionChangeHandlers: { + events: function (events, context) { + handleEventSources([events], context); + }, + eventSources: handleEventSources + } + }); + /* + BUG: if `event` was supplied, all previously-given `eventSources` will be wiped out + */ + function handleEventSources(inputs, context) { + var unfoundSources = hashValuesToArray(context.getCurrentData().eventSources); + var newInputs = []; + for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) { + var input = inputs_1[_i]; + var inputFound = false; + for (var i = 0; i < unfoundSources.length; i++) { + if (unfoundSources[i]._raw === input) { + unfoundSources.splice(i, 1); // delete + inputFound = true; + break; + } + } + if (!inputFound) { + newInputs.push(input); + } + } + for (var _a = 0, unfoundSources_1 = unfoundSources; _a < unfoundSources_1.length; _a++) { + var unfoundSource = unfoundSources_1[_a]; + context.dispatch({ + type: 'REMOVE_EVENT_SOURCE', + sourceId: unfoundSource.sourceId + }); + } + for (var _b = 0, newInputs_1 = newInputs; _b < newInputs_1.length; _b++) { + var newInput = newInputs_1[_b]; + context.calendarApi.addEventSource(newInput); + } + } + + function handleDateProfile(dateProfile, context) { + context.emitter.trigger('datesSet', __assign(__assign({}, buildRangeApiWithTimeZone(dateProfile.activeRange, context.dateEnv)), { view: context.viewApi })); + } + + function handleEventStore(eventStore, context) { + var emitter = context.emitter; + if (emitter.hasHandlers('eventsSet')) { + emitter.trigger('eventsSet', buildEventApis(eventStore, context)); + } + } + + /* + this array is exposed on the root namespace so that UMD plugins can add to it. + see the rollup-bundles script. + */ + var globalPlugins = [ + arrayEventSourcePlugin, + funcEventSourcePlugin, + jsonFeedEventSourcePlugin, + simpleRecurringEventsPlugin, + changeHandlerPlugin, + createPlugin({ + contentTypeHandlers: { + html: function () { return injectHtml; }, + domNodes: function () { return injectDomNodes; } + }, + propSetHandlers: { + dateProfile: handleDateProfile, + eventStore: handleEventStore + } + }) + ]; + function injectHtml(el, html) { + el.innerHTML = html; + } + function injectDomNodes(el, domNodes) { + var oldNodes = Array.prototype.slice.call(el.childNodes); // TODO: use array util + var newNodes = Array.prototype.slice.call(domNodes); // TODO: use array util + if (!isArraysEqual(oldNodes, newNodes)) { + for (var _i = 0, newNodes_1 = newNodes; _i < newNodes_1.length; _i++) { + var newNode = newNodes_1[_i]; + el.appendChild(newNode); + } + oldNodes.forEach(removeElement); + } + } + + var DelayedRunner = /** @class */ (function () { + function DelayedRunner(drainedOption) { + this.drainedOption = drainedOption; + this.isRunning = false; + this.isDirty = false; + this.pauseDepths = {}; + this.timeoutId = 0; + } + DelayedRunner.prototype.request = function (delay) { + this.isDirty = true; + if (!this.isPaused()) { + this.clearTimeout(); + if (delay == null) { + this.tryDrain(); + } + else { + this.timeoutId = setTimeout(// NOT OPTIMAL! TODO: look at debounce + this.tryDrain.bind(this), delay); + } + } + }; + DelayedRunner.prototype.pause = function (scope) { + if (scope === void 0) { scope = ''; } + var pauseDepths = this.pauseDepths; + pauseDepths[scope] = (pauseDepths[scope] || 0) + 1; + this.clearTimeout(); + }; + DelayedRunner.prototype.resume = function (scope, force) { + if (scope === void 0) { scope = ''; } + var pauseDepths = this.pauseDepths; + if (scope in pauseDepths) { + if (force) { + delete pauseDepths[scope]; + } + else { + var depth = --pauseDepths[scope]; + if (depth <= 0) { + delete pauseDepths[scope]; + } + } + this.tryDrain(); + } + }; + DelayedRunner.prototype.isPaused = function () { + return Object.keys(this.pauseDepths).length; + }; + DelayedRunner.prototype.tryDrain = function () { + if (!this.isRunning && !this.isPaused()) { + this.isRunning = true; + while (this.isDirty) { + this.isDirty = false; + this.drained(); // might set isDirty to true again + } + this.isRunning = false; + } + }; + DelayedRunner.prototype.clear = function () { + this.clearTimeout(); + this.isDirty = false; + this.pauseDepths = {}; + }; + DelayedRunner.prototype.clearTimeout = function () { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = 0; + } + }; + DelayedRunner.prototype.drained = function () { + if (this.drainedOption) { + this.drainedOption(); + } + }; + return DelayedRunner; + }()); + var TaskRunner = /** @class */ (function () { + function TaskRunner(runTaskOption, drainedOption) { + this.runTaskOption = runTaskOption; + this.drainedOption = drainedOption; + this.queue = []; + this.delayedRunner = new DelayedRunner(this.drain.bind(this)); + } + TaskRunner.prototype.request = function (task, delay) { + this.queue.push(task); + this.delayedRunner.request(delay); + }; + TaskRunner.prototype.pause = function (scope) { + this.delayedRunner.pause(scope); + }; + TaskRunner.prototype.resume = function (scope, force) { + this.delayedRunner.resume(scope, force); + }; + TaskRunner.prototype.drain = function () { + var queue = this.queue; + while (queue.length) { + var completedTasks = []; + var task = void 0; + while ((task = queue.shift())) { + this.runTask(task); + completedTasks.push(task); + } + this.drained(completedTasks); + } // keep going, in case new tasks were added in the drained handler + }; + TaskRunner.prototype.runTask = function (task) { + if (this.runTaskOption) { + this.runTaskOption(task); + } + }; + TaskRunner.prototype.drained = function (completedTasks) { + if (this.drainedOption) { + this.drainedOption(completedTasks); + } + }; + return TaskRunner; + }()); + + // Computes what the title at the top of the calendarApi should be for this view + function buildTitle(dateProfile, viewOptions, dateEnv) { + var range; + // for views that span a large unit of time, show the proper interval, ignoring stray days before and after + if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) { + range = dateProfile.currentRange; + } + else { // for day units or smaller, use the actual day range + range = dateProfile.activeRange; + } + return dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || buildTitleFormat(dateProfile)), { + isEndExclusive: dateProfile.isRangeAllDay, + defaultSeparator: viewOptions.titleRangeSeparator + }); + } + // Generates the format string that should be used to generate the title for the current date range. + // Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`. + function buildTitleFormat(dateProfile) { + var currentRangeUnit = dateProfile.currentRangeUnit; + if (currentRangeUnit === 'year') { + return { year: 'numeric' }; + } + else if (currentRangeUnit === 'month') { + return { year: 'numeric', month: 'long' }; // like "September 2014" + } + else { + var days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end); + if (days !== null && days > 1) { + // multi-day range. shorter, like "Sep 9 - 10 2014" + return { year: 'numeric', month: 'short', day: 'numeric' }; + } + else { + // one day. longer, like "September 9 2014" + return { year: 'numeric', month: 'long', day: 'numeric' }; + } + } + } + + // in future refactor, do the redux-style function(state=initial) for initial-state + // also, whatever is happening in constructor, have it happen in action queue too + var CalendarDataManager = /** @class */ (function () { + function CalendarDataManager(props) { + var _this = this; + this.computeOptionsData = memoize(this._computeOptionsData); + this.computeCurrentViewData = memoize(this._computeCurrentViewData); + this.organizeRawLocales = memoize(organizeRawLocales); + this.buildLocale = memoize(buildLocale); + this.buildPluginHooks = buildBuildPluginHooks(); + this.buildDateEnv = memoize(buildDateEnv$1); + this.buildTheme = memoize(buildTheme); + this.parseToolbars = memoize(parseToolbars); + this.buildViewSpecs = memoize(buildViewSpecs); + this.buildDateProfileGenerator = memoizeObjArg(buildDateProfileGenerator); + this.buildViewApi = memoize(buildViewApi); + this.buildViewUiProps = memoizeObjArg(buildViewUiProps); + this.buildEventUiBySource = memoize(buildEventUiBySource, isPropsEqual); + this.buildEventUiBases = memoize(buildEventUiBases); + this.parseContextBusinessHours = memoizeObjArg(parseContextBusinessHours); + this.buildTitle = memoize(buildTitle); + this.emitter = new Emitter(); + this.actionRunner = new TaskRunner(this._handleAction.bind(this), this.updateData.bind(this)); + this.currentCalendarOptionsInput = {}; + this.currentCalendarOptionsRefined = {}; + this.currentViewOptionsInput = {}; + this.currentViewOptionsRefined = {}; + this.currentCalendarOptionsRefiners = {}; + this.getCurrentData = function () { + return _this.data; + }; + this.dispatch = function (action) { + _this.actionRunner.request(action); // protects against recursive calls to _handleAction + }; + this.props = props; + this.actionRunner.pause(); + var dynamicOptionOverrides = {}; + var optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi); + var currentViewType = optionsData.calendarOptions.initialView || optionsData.pluginHooks.initialView; + var currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides); + // wire things up + // TODO: not DRY + props.calendarApi.currentDataManager = this; + this.emitter.setThisContext(props.calendarApi); + this.emitter.setOptions(currentViewData.options); + var currentDate = getInitialDate(optionsData.calendarOptions, optionsData.dateEnv); + var dateProfile = currentViewData.dateProfileGenerator.build(currentDate); + if (!rangeContainsMarker(dateProfile.activeRange, currentDate)) { + currentDate = dateProfile.currentRange.start; + } + var calendarContext = { + dateEnv: optionsData.dateEnv, + options: optionsData.calendarOptions, + pluginHooks: optionsData.pluginHooks, + calendarApi: props.calendarApi, + dispatch: this.dispatch, + emitter: this.emitter, + getCurrentData: this.getCurrentData + }; + // needs to be after setThisContext + for (var _i = 0, _a = optionsData.pluginHooks.contextInit; _i < _a.length; _i++) { + var callback = _a[_i]; + callback(calendarContext); + } + // NOT DRY + var eventSources = initEventSources(optionsData.calendarOptions, dateProfile, calendarContext); + var initialState = { + dynamicOptionOverrides: dynamicOptionOverrides, + currentViewType: currentViewType, + currentDate: currentDate, + dateProfile: dateProfile, + businessHours: this.parseContextBusinessHours(calendarContext), + eventSources: eventSources, + eventUiBases: {}, + loadingLevel: computeEventSourceLoadingLevel(eventSources), + eventStore: createEmptyEventStore(), + renderableEventStore: createEmptyEventStore(), + dateSelection: null, + eventSelection: '', + eventDrag: null, + eventResize: null, + selectionConfig: this.buildViewUiProps(calendarContext).selectionConfig + }; + var contextAndState = __assign(__assign({}, calendarContext), initialState); + for (var _b = 0, _c = optionsData.pluginHooks.reducers; _b < _c.length; _b++) { + var reducer = _c[_b]; + __assign(initialState, reducer(null, null, contextAndState)); + } + if (initialState.loadingLevel) { + this.emitter.trigger('loading', true); // NOT DRY + } + this.state = initialState; + this.updateData(); + this.actionRunner.resume(); + } + CalendarDataManager.prototype.resetOptions = function (optionOverrides, append) { + var props = this.props; + props.optionOverrides = append + ? __assign(__assign({}, props.optionOverrides), optionOverrides) : optionOverrides; + this.actionRunner.request({ + type: 'NOTHING' + }); + }; + CalendarDataManager.prototype._handleAction = function (action) { + var _a = this, props = _a.props, state = _a.state, emitter = _a.emitter; + var dynamicOptionOverrides = reduceDynamicOptionOverrides(state.dynamicOptionOverrides, action); + var optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi); + var currentViewType = reduceViewType(state.currentViewType, action); + var currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides); + // wire things up + // TODO: not DRY + props.calendarApi.currentDataManager = this; + emitter.setThisContext(props.calendarApi); + emitter.setOptions(currentViewData.options); + var calendarContext = { + dateEnv: optionsData.dateEnv, + options: optionsData.calendarOptions, + pluginHooks: optionsData.pluginHooks, + calendarApi: props.calendarApi, + dispatch: this.dispatch, + emitter: emitter, + getCurrentData: this.getCurrentData + }; + var currentDate = state.currentDate; + var dateProfile = state.dateProfile; + if (this.data && this.data.dateProfileGenerator !== currentViewData.dateProfileGenerator) { // hack + dateProfile = currentViewData.dateProfileGenerator.build(currentDate); + } + currentDate = reduceCurrentDate(currentDate, action); + dateProfile = reduceDateProfile(dateProfile, action, currentDate, currentViewData.dateProfileGenerator); + if (!rangeContainsMarker(dateProfile.currentRange, currentDate)) { + currentDate = dateProfile.currentRange.start; + } + var eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendarContext); + var eventSourceLoadingLevel = computeEventSourceLoadingLevel(eventSources); + var eventStore = reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendarContext); + var renderableEventStore = (eventSourceLoadingLevel && !currentViewData.options.progressiveEventRendering) ? + (state.renderableEventStore || eventStore) : // try from previous state + eventStore; + var _b = this.buildViewUiProps(calendarContext), eventUiSingleBase = _b.eventUiSingleBase, selectionConfig = _b.selectionConfig; // will memoize obj + var eventUiBySource = this.buildEventUiBySource(eventSources); + var eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource); + var prevLoadingLevel = state.loadingLevel || 0; + var loadingLevel = eventSourceLoadingLevel; + var newState = { + dynamicOptionOverrides: dynamicOptionOverrides, + currentViewType: currentViewType, + currentDate: currentDate, + dateProfile: dateProfile, + eventSources: eventSources, + eventStore: eventStore, + renderableEventStore: renderableEventStore, + selectionConfig: selectionConfig, + eventUiBases: eventUiBases, + loadingLevel: loadingLevel, + businessHours: this.parseContextBusinessHours(calendarContext), + dateSelection: reduceDateSelection(state.dateSelection, action), + eventSelection: reduceSelectedEvent(state.eventSelection, action), + eventDrag: reduceEventDrag(state.eventDrag, action), + eventResize: reduceEventResize(state.eventResize, action) + }; + var contextAndState = __assign(__assign({}, calendarContext), newState); + for (var _i = 0, _c = optionsData.pluginHooks.reducers; _i < _c.length; _i++) { + var reducer = _c[_i]; + __assign(newState, reducer(state, action, contextAndState)); // give the OLD state, for old value + } + // TODO: use propSetHandlers in plugin system + if (!prevLoadingLevel && loadingLevel) { + emitter.trigger('loading', true); + } + else if (prevLoadingLevel && !loadingLevel) { + emitter.trigger('loading', false); + } + this.state = newState; + if (props.onAction) { + props.onAction(action); + } + }; + CalendarDataManager.prototype.updateData = function () { + var _a = this, props = _a.props, state = _a.state; + var oldData = this.data; + var optionsData = this.computeOptionsData(props.optionOverrides, state.dynamicOptionOverrides, props.calendarApi); + var currentViewData = this.computeCurrentViewData(state.currentViewType, optionsData, props.optionOverrides, state.dynamicOptionOverrides); + var data = this.data = __assign(__assign(__assign({ viewTitle: this.buildTitle(state.dateProfile, currentViewData.options, optionsData.dateEnv), calendarApi: props.calendarApi, dispatch: this.dispatch, emitter: this.emitter, getCurrentData: this.getCurrentData }, optionsData), currentViewData), state); + var changeHandlers = optionsData.pluginHooks.optionChangeHandlers; + var oldCalendarOptions = oldData && oldData.calendarOptions; + var newCalendarOptions = optionsData.calendarOptions; + if (oldCalendarOptions && oldCalendarOptions !== newCalendarOptions) { + if (oldCalendarOptions.timeZone !== newCalendarOptions.timeZone) { + // hack + state.eventSources = data.eventSources = reduceEventSourcesNewTimeZone(data.eventSources, state.dateProfile, data); + state.eventStore = data.eventStore = rezoneEventStoreDates(data.eventStore, oldData.dateEnv, data.dateEnv); + } + for (var optionName in changeHandlers) { + if (oldCalendarOptions[optionName] !== newCalendarOptions[optionName]) { + changeHandlers[optionName](newCalendarOptions[optionName], data); + } + } + } + if (props.onData) { + props.onData(data); + } + }; + CalendarDataManager.prototype._computeOptionsData = function (optionOverrides, dynamicOptionOverrides, calendarApi) { + // TODO: blacklist options that are handled by optionChangeHandlers + var _a = this.processRawCalendarOptions(optionOverrides, dynamicOptionOverrides), refinedOptions = _a.refinedOptions, pluginHooks = _a.pluginHooks, localeDefaults = _a.localeDefaults, availableLocaleData = _a.availableLocaleData, extra = _a.extra; + warnUnknownOptions(extra); + var dateEnv = this.buildDateEnv(refinedOptions.timeZone, refinedOptions.locale, refinedOptions.weekNumberCalculation, refinedOptions.firstDay, refinedOptions.weekText, pluginHooks, availableLocaleData, refinedOptions.defaultRangeSeparator); + var viewSpecs = this.buildViewSpecs(pluginHooks.views, optionOverrides, dynamicOptionOverrides, localeDefaults); + var theme = this.buildTheme(refinedOptions, pluginHooks); + var toolbarConfig = this.parseToolbars(refinedOptions, optionOverrides, theme, viewSpecs, calendarApi); + return { + calendarOptions: refinedOptions, + pluginHooks: pluginHooks, + dateEnv: dateEnv, + viewSpecs: viewSpecs, + theme: theme, + toolbarConfig: toolbarConfig, + localeDefaults: localeDefaults, + availableRawLocales: availableLocaleData.map + }; + }; + // always called from behind a memoizer + CalendarDataManager.prototype.processRawCalendarOptions = function (optionOverrides, dynamicOptionOverrides) { + var _a = mergeRawOptions([ + BASE_OPTION_DEFAULTS, + optionOverrides, + dynamicOptionOverrides + ]), locales = _a.locales, locale = _a.locale; + var availableLocaleData = this.organizeRawLocales(locales); + var availableRawLocales = availableLocaleData.map; + var localeDefaults = this.buildLocale(locale || availableLocaleData.defaultCode, availableRawLocales).options; + var pluginHooks = this.buildPluginHooks(optionOverrides.plugins || [], globalPlugins); + var refiners = this.currentCalendarOptionsRefiners = __assign(__assign(__assign(__assign(__assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners); + var extra = {}; + var raw = mergeRawOptions([ + BASE_OPTION_DEFAULTS, + localeDefaults, + optionOverrides, + dynamicOptionOverrides + ]); + var refined = {}; + var currentRaw = this.currentCalendarOptionsInput; + var currentRefined = this.currentCalendarOptionsRefined; + var anyChanges = false; + for (var optionName in raw) { + if (optionName !== 'plugins') { // because plugins is special-cased + if (raw[optionName] === currentRaw[optionName] || + (COMPLEX_OPTION_COMPARATORS[optionName] && (optionName in currentRaw) && COMPLEX_OPTION_COMPARATORS[optionName](currentRaw[optionName], raw[optionName]))) { + refined[optionName] = currentRefined[optionName]; + } + else if (refiners[optionName]) { + refined[optionName] = refiners[optionName](raw[optionName]); + anyChanges = true; + } + else { + extra[optionName] = currentRaw[optionName]; + } + } + } + if (anyChanges) { + this.currentCalendarOptionsInput = raw; + this.currentCalendarOptionsRefined = refined; + } + return { + rawOptions: this.currentCalendarOptionsInput, + refinedOptions: this.currentCalendarOptionsRefined, + pluginHooks: pluginHooks, + availableLocaleData: availableLocaleData, + localeDefaults: localeDefaults, + extra: extra + }; + }; + CalendarDataManager.prototype._computeCurrentViewData = function (viewType, optionsData, optionOverrides, dynamicOptionOverrides) { + var viewSpec = optionsData.viewSpecs[viewType]; + if (!viewSpec) { + throw new Error("viewType \"" + viewType + "\" is not available. Please make sure you've loaded all neccessary plugins"); + } + var _a = this.processRawViewOptions(viewSpec, optionsData.pluginHooks, optionsData.localeDefaults, optionOverrides, dynamicOptionOverrides), refinedOptions = _a.refinedOptions, extra = _a.extra; + warnUnknownOptions(extra); + var dateProfileGenerator = this.buildDateProfileGenerator({ + dateProfileGeneratorClass: viewSpec.optionDefaults.dateProfileGeneratorClass, + duration: viewSpec.duration, + durationUnit: viewSpec.durationUnit, + usesMinMaxTime: viewSpec.optionDefaults.usesMinMaxTime, + dateEnv: optionsData.dateEnv, + calendarApi: this.props.calendarApi, + slotMinTime: refinedOptions.slotMinTime, + slotMaxTime: refinedOptions.slotMaxTime, + showNonCurrentDates: refinedOptions.showNonCurrentDates, + dayCount: refinedOptions.dayCount, + dateAlignment: refinedOptions.dateAlignment, + dateIncrement: refinedOptions.dateIncrement, + hiddenDays: refinedOptions.hiddenDays, + weekends: refinedOptions.weekends, + nowInput: refinedOptions.now, + validRangeInput: refinedOptions.validRange, + visibleRangeInput: refinedOptions.visibleRange, + monthMode: refinedOptions.monthMode, + fixedWeekCount: refinedOptions.fixedWeekCount + }); + var viewApi = this.buildViewApi(viewType, this.getCurrentData, optionsData.dateEnv); + return { viewSpec: viewSpec, options: refinedOptions, dateProfileGenerator: dateProfileGenerator, viewApi: viewApi }; + }; + CalendarDataManager.prototype.processRawViewOptions = function (viewSpec, pluginHooks, localeDefaults, optionOverrides, dynamicOptionOverrides) { + var raw = mergeRawOptions([ + BASE_OPTION_DEFAULTS, + viewSpec.optionDefaults, + localeDefaults, + optionOverrides, + viewSpec.optionOverrides, + dynamicOptionOverrides + ]); + var refiners = __assign(__assign(__assign(__assign(__assign(__assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), VIEW_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners); + var refined = {}; + var currentRaw = this.currentViewOptionsInput; + var currentRefined = this.currentViewOptionsRefined; + var anyChanges = false; + var extra = {}; + for (var optionName in raw) { + if (raw[optionName] === currentRaw[optionName]) { + refined[optionName] = currentRefined[optionName]; + } + else { + if (raw[optionName] === this.currentCalendarOptionsInput[optionName]) { + if (optionName in this.currentCalendarOptionsRefined) { // might be an "extra" prop + refined[optionName] = this.currentCalendarOptionsRefined[optionName]; + } + } + else if (refiners[optionName]) { + refined[optionName] = refiners[optionName](raw[optionName]); + } + else { + extra[optionName] = raw[optionName]; + } + anyChanges = true; + } + } + if (anyChanges) { + this.currentViewOptionsInput = raw; + this.currentViewOptionsRefined = refined; + } + return { + rawOptions: this.currentViewOptionsInput, + refinedOptions: this.currentViewOptionsRefined, + extra: extra + }; + }; + return CalendarDataManager; + }()); + function buildDateEnv$1(timeZone, explicitLocale, weekNumberCalculation, firstDay, weekText, pluginHooks, availableLocaleData, defaultSeparator) { + var locale = buildLocale(explicitLocale || availableLocaleData.defaultCode, availableLocaleData.map); + return new DateEnv({ + calendarSystem: 'gregory', + timeZone: timeZone, + namedTimeZoneImpl: pluginHooks.namedTimeZonedImpl, + locale: locale, + weekNumberCalculation: weekNumberCalculation, + firstDay: firstDay, + weekText: weekText, + cmdFormatter: pluginHooks.cmdFormatter, + defaultSeparator: defaultSeparator + }); + } + function buildTheme(options, pluginHooks) { + var ThemeClass = pluginHooks.themeClasses[options.themeSystem] || StandardTheme; + return new ThemeClass(options); + } + function buildDateProfileGenerator(props) { + var DateProfileGeneratorClass = props.dateProfileGeneratorClass || DateProfileGenerator; + return new DateProfileGeneratorClass(props); + } + function buildViewApi(type, getCurrentData, dateEnv) { + return new ViewApi(type, getCurrentData, dateEnv); + } + function buildEventUiBySource(eventSources) { + return mapHash(eventSources, function (eventSource) { + return eventSource.ui; + }); + } + function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) { + var eventUiBases = { '': eventUiSingleBase }; + for (var defId in eventDefs) { + var def = eventDefs[defId]; + if (def.sourceId && eventUiBySource[def.sourceId]) { + eventUiBases[defId] = eventUiBySource[def.sourceId]; + } + } + return eventUiBases; + } + function buildViewUiProps(calendarContext) { + var options = calendarContext.options; + return { + eventUiSingleBase: createEventUi({ + display: options.eventDisplay, + editable: options.editable, + startEditable: options.eventStartEditable, + durationEditable: options.eventDurationEditable, + constraint: options.eventConstraint, + overlap: typeof options.eventOverlap === 'boolean' ? options.eventOverlap : undefined, + allow: options.eventAllow, + backgroundColor: options.eventBackgroundColor, + borderColor: options.eventBorderColor, + textColor: options.eventTextColor, + color: options.eventColor + // classNames: options.eventClassNames // render hook will handle this + }, calendarContext), + selectionConfig: createEventUi({ + constraint: options.selectConstraint, + overlap: typeof options.selectOverlap === 'boolean' ? options.selectOverlap : undefined, + allow: options.selectAllow + }, calendarContext) + }; + } + function parseContextBusinessHours(calendarContext) { + return parseBusinessHours(calendarContext.options.businessHours, calendarContext); + } + function warnUnknownOptions(options, viewName) { + for (var optionName in options) { + console.warn("Unknown option '" + optionName + "'" + + (viewName ? " for view '" + viewName + "'" : '')); + } + } + + // TODO: move this to react plugin? + var CalendarDataProvider = /** @class */ (function (_super) { + __extends(CalendarDataProvider, _super); + function CalendarDataProvider(props) { + var _this = _super.call(this, props) || this; + _this.handleData = function (data) { + if (!_this.dataManager) { // still within initial run, before assignment in constructor + // eslint-disable-next-line react/no-direct-mutation-state + _this.state = data; // can't use setState yet + } + else { + _this.setState(data); + } + }; + _this.dataManager = new CalendarDataManager({ + optionOverrides: props.optionOverrides, + calendarApi: props.calendarApi, + onData: _this.handleData + }); + return _this; + } + CalendarDataProvider.prototype.render = function () { + return this.props.children(this.state); + }; + CalendarDataProvider.prototype.componentDidUpdate = function (prevProps) { + var newOptionOverrides = this.props.optionOverrides; + if (newOptionOverrides !== prevProps.optionOverrides) { // prevent recursive handleData + this.dataManager.resetOptions(newOptionOverrides); + } + }; + return CalendarDataProvider; + }(Component)); + + // HELPERS + /* + if nextDayThreshold is specified, slicing is done in an all-day fashion. + you can get nextDayThreshold from context.nextDayThreshold + */ + function sliceEvents(props, allDay) { + return sliceEventStore(props.eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? props.nextDayThreshold : null).fg; + } + + var NamedTimeZoneImpl = /** @class */ (function () { + function NamedTimeZoneImpl(timeZoneName) { + this.timeZoneName = timeZoneName; + } + return NamedTimeZoneImpl; + }()); + + var Interaction = /** @class */ (function () { + function Interaction(settings) { + this.component = settings.component; + } + Interaction.prototype.destroy = function () { + }; + return Interaction; + }()); + function parseInteractionSettings(component, input) { + return { + component: component, + el: input.el, + useEventCenter: input.useEventCenter != null ? input.useEventCenter : true + }; + } + function interactionSettingsToStore(settings) { + var _a; + return _a = {}, + _a[settings.component.uid] = settings, + _a; + } + // global state + var interactionSettingsStore = {}; + + /* + An abstraction for a dragging interaction originating on an event. + Does higher-level things than PointerDragger, such as possibly: + - a "mirror" that moves with the pointer + - a minimum number of pixels or other criteria for a true drag to begin + + subclasses must emit: + - pointerdown + - dragstart + - dragmove + - pointerup + - dragend + */ + var ElementDragging = /** @class */ (function () { + function ElementDragging(el, selector) { + this.emitter = new Emitter(); + } + ElementDragging.prototype.destroy = function () { + }; + ElementDragging.prototype.setMirrorIsVisible = function (bool) { + // optional if subclass doesn't want to support a mirror + }; + ElementDragging.prototype.setMirrorNeedsRevert = function (bool) { + // optional if subclass doesn't want to support a mirror + }; + ElementDragging.prototype.setAutoScrollEnabled = function (bool) { + // optional + }; + return ElementDragging; + }()); + + // TODO: get rid of this in favor of options system, + // tho it's really easy to access this globally rather than pass thru options. + var config = {}; + + /* + Information about what will happen when an external element is dragged-and-dropped + onto a calendar. Contains information for creating an event. + */ + var DRAG_META_REFINERS = { + startTime: createDuration, + duration: createDuration, + create: Boolean, + sourceId: String + }; + function parseDragMeta(raw) { + var _a = refineProps(raw, DRAG_META_REFINERS), refined = _a.refined, extra = _a.extra; + return { + startTime: refined.startTime || null, + duration: refined.duration || null, + create: refined.create != null ? refined.create : true, + sourceId: refined.sourceId, + leftoverProps: extra + }; + } + + var Toolbar = /** @class */ (function (_super) { + __extends(Toolbar, _super); + function Toolbar() { + return _super !== null && _super.apply(this, arguments) || this; + } + Toolbar.prototype.render = function () { + var _a = this.props, model = _a.model, extraClassName = _a.extraClassName; + var forceLtr = false; + var startContent, endContent; + var centerContent = model.center; + if (model.left) { + forceLtr = true; + startContent = model.left; + } + else { + startContent = model.start; + } + if (model.right) { + forceLtr = true; + endContent = model.right; + } + else { + endContent = model.end; + } + var classNames = [ + extraClassName || '', + 'fc-toolbar', + forceLtr ? 'fc-toolbar-ltr' : '' + ]; + return (createElement("div", { className: classNames.join(' ') }, + this.renderSection('start', startContent || []), + this.renderSection('center', centerContent || []), + this.renderSection('end', endContent || []))); + }; + Toolbar.prototype.renderSection = function (key, widgetGroups) { + var props = this.props; + return (createElement(ToolbarSection, { key: key, widgetGroups: widgetGroups, title: props.title, activeButton: props.activeButton, isTodayEnabled: props.isTodayEnabled, isPrevEnabled: props.isPrevEnabled, isNextEnabled: props.isNextEnabled })); + }; + return Toolbar; + }(BaseComponent)); + var ToolbarSection = /** @class */ (function (_super) { + __extends(ToolbarSection, _super); + function ToolbarSection() { + return _super !== null && _super.apply(this, arguments) || this; + } + ToolbarSection.prototype.render = function () { + var _this = this; + var children = this.props.widgetGroups.map(function (widgetGroup) { return _this.renderWidgetGroup(widgetGroup); }); + return createElement.apply(void 0, __spreadArrays(['div', { className: 'fc-toolbar-chunk' }], children)); + }; + ToolbarSection.prototype.renderWidgetGroup = function (widgetGroup) { + var props = this.props; + var theme = this.context.theme; + var children = []; + var isOnlyButtons = true; + for (var _i = 0, widgetGroup_1 = widgetGroup; _i < widgetGroup_1.length; _i++) { + var widget = widgetGroup_1[_i]; + var buttonName = widget.buttonName, buttonClick = widget.buttonClick, buttonText = widget.buttonText, buttonIcon = widget.buttonIcon; + if (buttonName === 'title') { + isOnlyButtons = false; + children.push(createElement("h2", { className: 'fc-toolbar-title' }, props.title)); + } + else { + var ariaAttrs = buttonIcon ? { 'aria-label': buttonName } : {}; + var buttonClasses = ['fc-' + buttonName + '-button', theme.getClass('button')]; + if (buttonName === props.activeButton) { + buttonClasses.push(theme.getClass('buttonActive')); + } + var isDisabled = (!props.isTodayEnabled && buttonName === 'today') || + (!props.isPrevEnabled && buttonName === 'prev') || + (!props.isNextEnabled && buttonName === 'next'); + children.push(createElement("button", __assign({ disabled: isDisabled, className: buttonClasses.join(' '), onClick: buttonClick, type: 'button' }, ariaAttrs), buttonText || (buttonIcon ? createElement("span", { className: buttonIcon }) : ''))); + } + } + if (children.length > 1) { + var groupClassName = (isOnlyButtons && theme.getClass('buttonGroup')) || ''; + return createElement.apply(void 0, __spreadArrays(['div', { className: groupClassName }], children)); + } + else { + return children[0]; + } + }; + return ToolbarSection; + }(BaseComponent)); + + // TODO: do function component? + var ViewContainer = /** @class */ (function (_super) { + __extends(ViewContainer, _super); + function ViewContainer() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.state = { + availableWidth: null + }; + _this.handleEl = function (el) { + _this.el = el; + setRef(_this.props.elRef, el); + _this.updateAvailableWidth(); + }; + _this.handleResize = function () { + _this.updateAvailableWidth(); + }; + return _this; + } + ViewContainer.prototype.render = function () { + var _a = this, props = _a.props, state = _a.state; + var aspectRatio = props.aspectRatio; + var classNames = [ + 'fc-view-harness', + (aspectRatio || props.liquid || props.height) + ? 'fc-view-harness-active' // harness controls the height + : 'fc-view-harness-passive' // let the view do the height + ]; + var height = ''; + var paddingBottom = ''; + if (aspectRatio) { + if (state.availableWidth !== null) { + height = state.availableWidth / aspectRatio; + } + else { + // while waiting to know availableWidth, we can't set height to *zero* + // because will cause lots of unnecessary scrollbars within scrollgrid. + // BETTER: don't start rendering ANYTHING yet until we know container width + // NOTE: why not always use paddingBottom? Causes height oscillation (issue 5606) + paddingBottom = (1 / aspectRatio) * 100 + '%'; + } + } + else { + height = props.height || ''; + } + return (createElement("div", { ref: this.handleEl, onClick: props.onClick, className: classNames.join(' '), style: { height: height, paddingBottom: paddingBottom } }, props.children)); + }; + ViewContainer.prototype.componentDidMount = function () { + this.context.addResizeHandler(this.handleResize); + }; + ViewContainer.prototype.componentWillUnmount = function () { + this.context.removeResizeHandler(this.handleResize); + }; + ViewContainer.prototype.updateAvailableWidth = function () { + if (this.el && // needed. but why? + this.props.aspectRatio // aspectRatio is the only height setting that needs availableWidth + ) { + this.setState({ availableWidth: this.el.offsetWidth }); + } + }; + return ViewContainer; + }(BaseComponent)); + + /* + Detects when the user clicks on an event within a DateComponent + */ + var EventClicking = /** @class */ (function (_super) { + __extends(EventClicking, _super); + function EventClicking(settings) { + var _this = _super.call(this, settings) || this; + _this.handleSegClick = function (ev, segEl) { + var component = _this.component; + var context = component.context; + var seg = getElSeg(segEl); + if (seg && // might be the
surrounding the more link + component.isValidSegDownEl(ev.target)) { + // our way to simulate a link click for elements that can't be tags + // grab before trigger fired in case trigger trashes DOM thru rerendering + var hasUrlContainer = elementClosest(ev.target, '.fc-event-forced-url'); + var url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : ''; + context.emitter.trigger('eventClick', { + el: segEl, + event: new EventApi(component.context, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: context.viewApi + }); + if (url && !ev.defaultPrevented) { + window.location.href = url; + } + } + }; + _this.destroy = listenBySelector(settings.el, 'click', '.fc-event', // on both fg and bg events + _this.handleSegClick); + return _this; + } + return EventClicking; + }(Interaction)); + + /* + Triggers events and adds/removes core classNames when the user's pointer + enters/leaves event-elements of a component. + */ + var EventHovering = /** @class */ (function (_super) { + __extends(EventHovering, _super); + function EventHovering(settings) { + var _this = _super.call(this, settings) || this; + // for simulating an eventMouseLeave when the event el is destroyed while mouse is over it + _this.handleEventElRemove = function (el) { + if (el === _this.currentSegEl) { + _this.handleSegLeave(null, _this.currentSegEl); + } + }; + _this.handleSegEnter = function (ev, segEl) { + if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper + _this.currentSegEl = segEl; + _this.triggerEvent('eventMouseEnter', ev, segEl); + } + }; + _this.handleSegLeave = function (ev, segEl) { + if (_this.currentSegEl) { + _this.currentSegEl = null; + _this.triggerEvent('eventMouseLeave', ev, segEl); + } + }; + _this.removeHoverListeners = listenToHoverBySelector(settings.el, '.fc-event', // on both fg and bg events + _this.handleSegEnter, _this.handleSegLeave); + return _this; + } + EventHovering.prototype.destroy = function () { + this.removeHoverListeners(); + }; + EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) { + var component = this.component; + var context = component.context; + var seg = getElSeg(segEl); + if (!ev || component.isValidSegDownEl(ev.target)) { + context.emitter.trigger(publicEvName, { + el: segEl, + event: new EventApi(context, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: context.viewApi + }); + } + }; + return EventHovering; + }(Interaction)); + + var CalendarContent = /** @class */ (function (_super) { + __extends(CalendarContent, _super); + function CalendarContent() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.buildViewContext = memoize(buildViewContext); + _this.buildViewPropTransformers = memoize(buildViewPropTransformers); + _this.buildToolbarProps = memoize(buildToolbarProps); + _this.handleNavLinkClick = buildDelegationHandler('a[data-navlink]', _this._handleNavLinkClick.bind(_this)); + _this.headerRef = createRef(); + _this.footerRef = createRef(); + _this.interactionsStore = {}; + // Component Registration + // ----------------------------------------------------------------------------------------------------------------- + _this.registerInteractiveComponent = function (component, settingsInput) { + var settings = parseInteractionSettings(component, settingsInput); + var DEFAULT_INTERACTIONS = [ + EventClicking, + EventHovering + ]; + var interactionClasses = DEFAULT_INTERACTIONS.concat(_this.props.pluginHooks.componentInteractions); + var interactions = interactionClasses.map(function (interactionClass) { + return new interactionClass(settings); + }); + _this.interactionsStore[component.uid] = interactions; + interactionSettingsStore[component.uid] = settings; + }; + _this.unregisterInteractiveComponent = function (component) { + for (var _i = 0, _a = _this.interactionsStore[component.uid]; _i < _a.length; _i++) { + var listener = _a[_i]; + listener.destroy(); + } + delete _this.interactionsStore[component.uid]; + delete interactionSettingsStore[component.uid]; + }; + // Resizing + // ----------------------------------------------------------------------------------------------------------------- + _this.resizeRunner = new DelayedRunner(function () { + _this.props.emitter.trigger('_resize', true); // should window resizes be considered "forced" ? + _this.props.emitter.trigger('windowResize', { view: _this.props.viewApi }); + }); + _this.handleWindowResize = function (ev) { + var options = _this.props.options; + if (options.handleWindowResize && + ev.target === window // avoid jqui events + ) { + _this.resizeRunner.request(options.windowResizeDelay); + } + }; + return _this; + } + /* + renders INSIDE of an outer div + */ + CalendarContent.prototype.render = function () { + var props = this.props; + var toolbarConfig = props.toolbarConfig, options = props.options; + var toolbarProps = this.buildToolbarProps(props.viewSpec, props.dateProfile, props.dateProfileGenerator, props.currentDate, getNow(props.options.now, props.dateEnv), // TODO: use NowTimer???? + props.viewTitle); + var viewVGrow = false; + var viewHeight = ''; + var viewAspectRatio; + if (props.isHeightAuto || props.forPrint) { + viewHeight = ''; + } + else if (options.height != null) { + viewVGrow = true; + } + else if (options.contentHeight != null) { + viewHeight = options.contentHeight; + } + else { + viewAspectRatio = Math.max(options.aspectRatio, 0.5); // prevent from getting too tall + } + var viewContext = this.buildViewContext(props.viewSpec, props.viewApi, props.options, props.dateProfileGenerator, props.dateEnv, props.theme, props.pluginHooks, props.dispatch, props.getCurrentData, props.emitter, props.calendarApi, this.registerInteractiveComponent, this.unregisterInteractiveComponent); + return (createElement(ViewContextType.Provider, { value: viewContext }, + toolbarConfig.headerToolbar && + createElement(Toolbar, __assign({ ref: this.headerRef, extraClassName: 'fc-header-toolbar', model: toolbarConfig.headerToolbar }, toolbarProps)), + createElement(ViewContainer, { liquid: viewVGrow, height: viewHeight, aspectRatio: viewAspectRatio, onClick: this.handleNavLinkClick }, + this.renderView(props), + this.buildAppendContent()), + toolbarConfig.footerToolbar && + createElement(Toolbar, __assign({ ref: this.footerRef, extraClassName: 'fc-footer-toolbar', model: toolbarConfig.footerToolbar }, toolbarProps)))); + }; + CalendarContent.prototype.componentDidMount = function () { + var props = this.props; + this.calendarInteractions = props.pluginHooks.calendarInteractions + .map(function (calendarInteractionClass) { + return new calendarInteractionClass(props); + }); + window.addEventListener('resize', this.handleWindowResize); + var propSetHandlers = props.pluginHooks.propSetHandlers; + for (var propName in propSetHandlers) { + propSetHandlers[propName](props[propName], props); + } + }; + CalendarContent.prototype.componentDidUpdate = function (prevProps) { + var props = this.props; + var propSetHandlers = props.pluginHooks.propSetHandlers; + for (var propName in propSetHandlers) { + if (props[propName] !== prevProps[propName]) { + propSetHandlers[propName](props[propName], props); + } + } + }; + CalendarContent.prototype.componentWillUnmount = function () { + window.removeEventListener('resize', this.handleWindowResize); + this.resizeRunner.clear(); + for (var _i = 0, _a = this.calendarInteractions; _i < _a.length; _i++) { + var interaction = _a[_i]; + interaction.destroy(); + } + this.props.emitter.trigger('_unmount'); + }; + CalendarContent.prototype._handleNavLinkClick = function (ev, anchorEl) { + var _a = this.props, dateEnv = _a.dateEnv, options = _a.options, calendarApi = _a.calendarApi; + var navLinkOptions = anchorEl.getAttribute('data-navlink'); + navLinkOptions = navLinkOptions ? JSON.parse(navLinkOptions) : {}; + var dateMarker = dateEnv.createMarker(navLinkOptions.date); + var viewType = navLinkOptions.type; + var customAction = viewType === 'day' ? options.navLinkDayClick : + viewType === 'week' ? options.navLinkWeekClick : null; + if (typeof customAction === 'function') { + customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev); + } + else { + if (typeof customAction === 'string') { + viewType = customAction; + } + calendarApi.zoomTo(dateMarker, viewType); + } + }; + CalendarContent.prototype.buildAppendContent = function () { + var props = this.props; + var children = props.pluginHooks.viewContainerAppends.map(function (buildAppendContent) { return buildAppendContent(props); }); + return createElement.apply(void 0, __spreadArrays([Fragment, {}], children)); + }; + CalendarContent.prototype.renderView = function (props) { + var pluginHooks = props.pluginHooks; + var viewSpec = props.viewSpec; + var viewProps = { + dateProfile: props.dateProfile, + businessHours: props.businessHours, + eventStore: props.renderableEventStore, + eventUiBases: props.eventUiBases, + dateSelection: props.dateSelection, + eventSelection: props.eventSelection, + eventDrag: props.eventDrag, + eventResize: props.eventResize, + isHeightAuto: props.isHeightAuto, + forPrint: props.forPrint + }; + var transformers = this.buildViewPropTransformers(pluginHooks.viewPropsTransformers); + for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { + var transformer = transformers_1[_i]; + __assign(viewProps, transformer.transform(viewProps, props)); + } + var ViewComponent = viewSpec.component; + return (createElement(ViewComponent, __assign({}, viewProps))); + }; + return CalendarContent; + }(PureComponent)); + function buildToolbarProps(viewSpec, dateProfile, dateProfileGenerator, currentDate, now, title) { + // don't force any date-profiles to valid date profiles (the `false`) so that we can tell if it's invalid + var todayInfo = dateProfileGenerator.build(now, undefined, false); // TODO: need `undefined` or else INFINITE LOOP for some reason + var prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate, false); + var nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate, false); + return { + title: title, + activeButton: viewSpec.type, + isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now), + isPrevEnabled: prevInfo.isValid, + isNextEnabled: nextInfo.isValid + }; + } + // Plugin + // ----------------------------------------------------------------------------------------------------------------- + function buildViewPropTransformers(theClasses) { + return theClasses.map(function (theClass) { + return new theClass(); + }); + } + + var CalendarRoot = /** @class */ (function (_super) { + __extends(CalendarRoot, _super); + function CalendarRoot() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.state = { + forPrint: false + }; + _this.handleBeforePrint = function () { + _this.setState({ forPrint: true }); + }; + _this.handleAfterPrint = function () { + _this.setState({ forPrint: false }); + }; + return _this; + } + CalendarRoot.prototype.render = function () { + var props = this.props; + var options = props.options; + var forPrint = this.state.forPrint; + var isHeightAuto = forPrint || options.height === 'auto' || options.contentHeight === 'auto'; + var height = (!isHeightAuto && options.height != null) ? options.height : ''; + var classNames = [ + 'fc', + forPrint ? 'fc-media-print' : 'fc-media-screen', + 'fc-direction-' + options.direction, + props.theme.getClass('root') + ]; + if (!getCanVGrowWithinCell()) { + classNames.push('fc-liquid-hack'); + } + return props.children(classNames, height, isHeightAuto, forPrint); + }; + CalendarRoot.prototype.componentDidMount = function () { + var emitter = this.props.emitter; + emitter.on('_beforeprint', this.handleBeforePrint); + emitter.on('_afterprint', this.handleAfterPrint); + }; + CalendarRoot.prototype.componentWillUnmount = function () { + var emitter = this.props.emitter; + emitter.off('_beforeprint', this.handleBeforePrint); + emitter.off('_afterprint', this.handleAfterPrint); + }; + return CalendarRoot; + }(BaseComponent)); + + // Computes a default column header formatting string if `colFormat` is not explicitly defined + function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) { + // if more than one week row, or if there are a lot of columns with not much space, + // put just the day numbers will be in each cell + if (!datesRepDistinctDays || dayCnt > 10) { + return createFormatter({ weekday: 'short' }); // "Sat" + } + else if (dayCnt > 1) { + return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12" + } + else { + return createFormatter({ weekday: 'long' }); // "Saturday" + } + } + + var CLASS_NAME = 'fc-col-header-cell'; // do the cushion too? no + var TableDateCell = /** @class */ (function (_super) { + __extends(TableDateCell, _super); + function TableDateCell() { + return _super !== null && _super.apply(this, arguments) || this; + } + TableDateCell.prototype.render = function () { + var _a = this.context, dateEnv = _a.dateEnv, options = _a.options, theme = _a.theme, viewApi = _a.viewApi; + var props = this.props; + var date = props.date, dateProfile = props.dateProfile; + var dayMeta = getDateMeta(date, props.todayRange, null, dateProfile); + var classNames = [CLASS_NAME].concat(getDayClassNames(dayMeta, theme)); + var text = dateEnv.format(date, props.dayHeaderFormat); + // if colCnt is 1, we are already in a day-view and don't need a navlink + var navLinkAttrs = (options.navLinks && !dayMeta.isDisabled && props.colCnt > 1) + ? { 'data-navlink': buildNavLinkData(date), tabIndex: 0 } + : {}; + var hookProps = __assign(__assign(__assign({ date: dateEnv.toDate(date), view: viewApi }, props.extraHookProps), { text: text }), dayMeta); + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInner, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("th", __assign({ ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-date": !dayMeta.isDisabled ? formatDayString(date) : undefined, colSpan: props.colSpan }, props.extraDataAttrs), + createElement("div", { className: 'fc-scrollgrid-sync-inner' }, !dayMeta.isDisabled && + createElement("a", __assign({ ref: innerElRef, className: [ + 'fc-col-header-cell-cushion', + props.isSticky ? 'fc-sticky' : '' + ].join(' ') }, navLinkAttrs), innerContent)))); })); + }; + return TableDateCell; + }(BaseComponent)); + var TableDowCell = /** @class */ (function (_super) { + __extends(TableDowCell, _super); + function TableDowCell() { + return _super !== null && _super.apply(this, arguments) || this; + } + TableDowCell.prototype.render = function () { + var props = this.props; + var _a = this.context, dateEnv = _a.dateEnv, theme = _a.theme, viewApi = _a.viewApi, options = _a.options; + var date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT + var dateMeta = { + dow: props.dow, + isDisabled: false, + isFuture: false, + isPast: false, + isToday: false, + isOther: false + }; + var classNames = [CLASS_NAME].concat(getDayClassNames(dateMeta, theme), props.extraClassNames || []); + var text = dateEnv.format(date, props.dayHeaderFormat); + var hookProps = __assign(__assign(__assign(__assign({ // TODO: make this public? + date: date }, dateMeta), { view: viewApi }), props.extraHookProps), { text: text }); + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInner, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("th", __assign({ ref: rootElRef, className: classNames.concat(customClassNames).join(' '), colSpan: props.colSpan }, props.extraDataAttrs), + createElement("div", { className: 'fc-scrollgrid-sync-inner' }, + createElement("a", { className: [ + 'fc-col-header-cell-cushion', + props.isSticky ? 'fc-sticky' : '' + ].join(' '), ref: innerElRef }, innerContent)))); })); + }; + return TableDowCell; + }(BaseComponent)); + function renderInner(hookProps) { + return hookProps.text; + } + + var NowTimer = /** @class */ (function (_super) { + __extends(NowTimer, _super); + function NowTimer(props, context) { + var _this = _super.call(this, props, context) || this; + _this.initialNowDate = getNow(context.options.now, context.dateEnv); + _this.initialNowQueriedMs = new Date().valueOf(); + _this.state = _this.computeTiming().currentState; + return _this; + } + NowTimer.prototype.render = function () { + var _a = this, props = _a.props, state = _a.state; + return props.children(state.nowDate, state.todayRange); + }; + NowTimer.prototype.componentDidMount = function () { + this.setTimeout(); + }; + NowTimer.prototype.componentDidUpdate = function (prevProps) { + if (prevProps.unit !== this.props.unit) { + this.clearTimeout(); + this.setTimeout(); + } + }; + NowTimer.prototype.componentWillUnmount = function () { + this.clearTimeout(); + }; + NowTimer.prototype.computeTiming = function () { + var _a = this, props = _a.props, context = _a.context; + var unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs); + var currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit); + var nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit)); + var waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf(); + return { + currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) }, + nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) }, + waitMs: waitMs + }; + }; + NowTimer.prototype.setTimeout = function () { + var _this = this; + var _a = this.computeTiming(), nextState = _a.nextState, waitMs = _a.waitMs; + this.timeoutId = setTimeout(function () { + _this.setState(nextState, function () { + _this.setTimeout(); + }); + }, waitMs); + }; + NowTimer.prototype.clearTimeout = function () { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + }; + NowTimer.contextType = ViewContextType; + return NowTimer; + }(Component)); + function buildDayRange(date) { + var start = startOfDay(date); + var end = addDays(start, 1); + return { start: start, end: end }; + } + + var DayHeader = /** @class */ (function (_super) { + __extends(DayHeader, _super); + function DayHeader() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.createDayHeaderFormatter = memoize(createDayHeaderFormatter); + return _this; + } + DayHeader.prototype.render = function () { + var context = this.context; + var _a = this.props, dates = _a.dates, dateProfile = _a.dateProfile, datesRepDistinctDays = _a.datesRepDistinctDays, renderIntro = _a.renderIntro; + var dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dates.length); + return (createElement(NowTimer, { unit: 'day' }, function (nowDate, todayRange) { return (createElement("tr", null, + renderIntro && renderIntro(), + dates.map(function (date) { return (datesRepDistinctDays ? + createElement(TableDateCell, { key: date.toISOString(), date: date, dateProfile: dateProfile, todayRange: todayRange, colCnt: dates.length, dayHeaderFormat: dayHeaderFormat }) : + createElement(TableDowCell, { key: date.getUTCDay(), dow: date.getUTCDay(), dayHeaderFormat: dayHeaderFormat })); }))); })); + }; + return DayHeader; + }(BaseComponent)); + function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) { + return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt); + } + + var DaySeriesModel = /** @class */ (function () { + function DaySeriesModel(range, dateProfileGenerator) { + var date = range.start; + var end = range.end; + var indices = []; + var dates = []; + var dayIndex = -1; + while (date < end) { // loop each day from start to end + if (dateProfileGenerator.isHiddenDay(date)) { + indices.push(dayIndex + 0.5); // mark that it's between indices + } + else { + dayIndex++; + indices.push(dayIndex); + dates.push(date); + } + date = addDays(date, 1); + } + this.dates = dates; + this.indices = indices; + this.cnt = dates.length; + } + DaySeriesModel.prototype.sliceRange = function (range) { + var firstIndex = this.getDateDayIndex(range.start); // inclusive first index + var lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index + var clippedFirstIndex = Math.max(0, firstIndex); + var clippedLastIndex = Math.min(this.cnt - 1, lastIndex); + // deal with in-between indices + clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell + clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell + if (clippedFirstIndex <= clippedLastIndex) { + return { + firstIndex: clippedFirstIndex, + lastIndex: clippedLastIndex, + isStart: firstIndex === clippedFirstIndex, + isEnd: lastIndex === clippedLastIndex + }; + } + else { + return null; + } + }; + // Given a date, returns its chronolocial cell-index from the first cell of the grid. + // If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets. + // If before the first offset, returns a negative number. + // If after the last offset, returns an offset past the last cell offset. + // Only works for *start* dates of cells. Will not work for exclusive end dates for cells. + DaySeriesModel.prototype.getDateDayIndex = function (date) { + var indices = this.indices; + var dayOffset = Math.floor(diffDays(this.dates[0], date)); + if (dayOffset < 0) { + return indices[0] - 1; + } + else if (dayOffset >= indices.length) { + return indices[indices.length - 1] + 1; + } + else { + return indices[dayOffset]; + } + }; + return DaySeriesModel; + }()); + + var DayTableModel = /** @class */ (function () { + function DayTableModel(daySeries, breakOnWeeks) { + var dates = daySeries.dates; + var daysPerRow; + var firstDay; + var rowCnt; + if (breakOnWeeks) { + // count columns until the day-of-week repeats + firstDay = dates[0].getUTCDay(); + for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow++) { + if (dates[daysPerRow].getUTCDay() === firstDay) { + break; + } + } + rowCnt = Math.ceil(dates.length / daysPerRow); + } + else { + rowCnt = 1; + daysPerRow = dates.length; + } + this.rowCnt = rowCnt; + this.colCnt = daysPerRow; + this.daySeries = daySeries; + this.cells = this.buildCells(); + this.headerDates = this.buildHeaderDates(); + } + DayTableModel.prototype.buildCells = function () { + var rows = []; + for (var row = 0; row < this.rowCnt; row++) { + var cells = []; + for (var col = 0; col < this.colCnt; col++) { + cells.push(this.buildCell(row, col)); + } + rows.push(cells); + } + return rows; + }; + DayTableModel.prototype.buildCell = function (row, col) { + var date = this.daySeries.dates[row * this.colCnt + col]; + return { + key: date.toISOString(), + date: date + }; + }; + DayTableModel.prototype.buildHeaderDates = function () { + var dates = []; + for (var col = 0; col < this.colCnt; col++) { + dates.push(this.cells[0][col].date); + } + return dates; + }; + DayTableModel.prototype.sliceRange = function (range) { + var colCnt = this.colCnt; + var seriesSeg = this.daySeries.sliceRange(range); + var segs = []; + if (seriesSeg) { + var firstIndex = seriesSeg.firstIndex, lastIndex = seriesSeg.lastIndex; + var index = firstIndex; + while (index <= lastIndex) { + var row = Math.floor(index / colCnt); + var nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1); + segs.push({ + row: row, + firstCol: index % colCnt, + lastCol: (nextIndex - 1) % colCnt, + isStart: seriesSeg.isStart && index === firstIndex, + isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex + }); + index = nextIndex; + } + } + return segs; + }; + return DayTableModel; + }()); + + var Slicer = /** @class */ (function () { + function Slicer() { + this.sliceBusinessHours = memoize(this._sliceBusinessHours); + this.sliceDateSelection = memoize(this._sliceDateSpan); + this.sliceEventStore = memoize(this._sliceEventStore); + this.sliceEventDrag = memoize(this._sliceInteraction); + this.sliceEventResize = memoize(this._sliceInteraction); + this.forceDayIfListItem = false; // hack + } + Slicer.prototype.sliceProps = function (props, dateProfile, nextDayThreshold, context) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + var eventUiBases = props.eventUiBases; + var eventSegs = this.sliceEventStore.apply(this, __spreadArrays([props.eventStore, eventUiBases, dateProfile, nextDayThreshold], extraArgs)); + return { + dateSelectionSegs: this.sliceDateSelection.apply(this, __spreadArrays([props.dateSelection, eventUiBases, context], extraArgs)), + businessHourSegs: this.sliceBusinessHours.apply(this, __spreadArrays([props.businessHours, dateProfile, nextDayThreshold, context], extraArgs)), + fgEventSegs: eventSegs.fg, + bgEventSegs: eventSegs.bg, + eventDrag: this.sliceEventDrag.apply(this, __spreadArrays([props.eventDrag, eventUiBases, dateProfile, nextDayThreshold], extraArgs)), + eventResize: this.sliceEventResize.apply(this, __spreadArrays([props.eventResize, eventUiBases, dateProfile, nextDayThreshold], extraArgs)), + eventSelection: props.eventSelection + }; // TODO: give interactionSegs? + }; + Slicer.prototype.sliceNowDate = function (// does not memoize + date, context) { + var extraArgs = []; + for (var _i = 2; _i < arguments.length; _i++) { + extraArgs[_i - 2] = arguments[_i]; + } + return this._sliceDateSpan.apply(this, __spreadArrays([{ range: { start: date, end: addMs(date, 1) }, allDay: false }, + {}, + context], extraArgs)); + }; + Slicer.prototype._sliceBusinessHours = function (businessHours, dateProfile, nextDayThreshold, context) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + if (!businessHours) { + return []; + } + return this._sliceEventStore.apply(this, __spreadArrays([expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), context), + {}, + dateProfile, + nextDayThreshold], extraArgs)).bg; + }; + Slicer.prototype._sliceEventStore = function (eventStore, eventUiBases, dateProfile, nextDayThreshold) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + if (eventStore) { + var rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); + return { + bg: this.sliceEventRanges(rangeRes.bg, extraArgs), + fg: this.sliceEventRanges(rangeRes.fg, extraArgs) + }; + } + else { + return { bg: [], fg: [] }; + } + }; + Slicer.prototype._sliceInteraction = function (interaction, eventUiBases, dateProfile, nextDayThreshold) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + if (!interaction) { + return null; + } + var rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); + return { + segs: this.sliceEventRanges(rangeRes.fg, extraArgs), + affectedInstances: interaction.affectedEvents.instances, + isEvent: interaction.isEvent + }; + }; + Slicer.prototype._sliceDateSpan = function (dateSpan, eventUiBases, context) { + var extraArgs = []; + for (var _i = 3; _i < arguments.length; _i++) { + extraArgs[_i - 3] = arguments[_i]; + } + if (!dateSpan) { + return []; + } + var eventRange = fabricateEventRange(dateSpan, eventUiBases, context); + var segs = this.sliceRange.apply(this, __spreadArrays([dateSpan.range], extraArgs)); + for (var _a = 0, segs_1 = segs; _a < segs_1.length; _a++) { + var seg = segs_1[_a]; + seg.eventRange = eventRange; + } + return segs; + }; + /* + "complete" seg means it has component and eventRange + */ + Slicer.prototype.sliceEventRanges = function (eventRanges, extraArgs) { + var segs = []; + for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { + var eventRange = eventRanges_1[_i]; + segs.push.apply(segs, this.sliceEventRange(eventRange, extraArgs)); + } + return segs; + }; + /* + "complete" seg means it has component and eventRange + */ + Slicer.prototype.sliceEventRange = function (eventRange, extraArgs) { + var dateRange = eventRange.range; + // hack to make multi-day events that are being force-displayed as list-items to take up only one day + if (this.forceDayIfListItem && eventRange.ui.display === 'list-item') { + dateRange = { + start: dateRange.start, + end: addDays(dateRange.start, 1) + }; + } + var segs = this.sliceRange.apply(this, __spreadArrays([dateRange], extraArgs)); + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + seg.eventRange = eventRange; + seg.isStart = eventRange.isStart && seg.isStart; + seg.isEnd = eventRange.isEnd && seg.isEnd; + } + return segs; + }; + return Slicer; + }()); + /* + for incorporating slotMinTime/slotMaxTime if appropriate + TODO: should be part of DateProfile! + TimelineDateProfile already does this btw + */ + function computeActiveRange(dateProfile, isComponentAllDay) { + var range = dateProfile.activeRange; + if (isComponentAllDay) { + return range; + } + return { + start: addMs(range.start, dateProfile.slotMinTime.milliseconds), + end: addMs(range.end, dateProfile.slotMaxTime.milliseconds - 864e5) // 864e5 = ms in a day + }; + } + + var VISIBLE_HIDDEN_RE = /^(visible|hidden)$/; + var Scroller = /** @class */ (function (_super) { + __extends(Scroller, _super); + function Scroller() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.handleEl = function (el) { + _this.el = el; + setRef(_this.props.elRef, el); + }; + return _this; + } + Scroller.prototype.render = function () { + var props = this.props; + var liquid = props.liquid, liquidIsAbsolute = props.liquidIsAbsolute; + var isAbsolute = liquid && liquidIsAbsolute; + var className = ['fc-scroller']; + if (liquid) { + if (liquidIsAbsolute) { + className.push('fc-scroller-liquid-absolute'); + } + else { + className.push('fc-scroller-liquid'); + } + } + return (createElement("div", { ref: this.handleEl, className: className.join(' '), style: { + overflowX: props.overflowX, + overflowY: props.overflowY, + left: (isAbsolute && -(props.overcomeLeft || 0)) || '', + right: (isAbsolute && -(props.overcomeRight || 0)) || '', + bottom: (isAbsolute && -(props.overcomeBottom || 0)) || '', + marginLeft: (!isAbsolute && -(props.overcomeLeft || 0)) || '', + marginRight: (!isAbsolute && -(props.overcomeRight || 0)) || '', + marginBottom: (!isAbsolute && -(props.overcomeBottom || 0)) || '', + maxHeight: props.maxHeight || '' + } }, props.children)); + }; + Scroller.prototype.needsXScrolling = function () { + if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) { + return false; + } + // testing scrollWidth>clientWidth is unreliable cross-browser when pixel heights aren't integers. + // much more reliable to see if children are taller than the scroller, even tho doesn't account for + // inner-child margins and absolute positioning + var el = this.el; + var realClientWidth = this.el.getBoundingClientRect().width - this.getYScrollbarWidth(); + var children = el.children; + for (var i = 0; i < children.length; i++) { + var childEl = children[i]; + if (childEl.getBoundingClientRect().width > realClientWidth) { + return true; + } + } + return false; + }; + Scroller.prototype.needsYScrolling = function () { + if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) { + return false; + } + // testing scrollHeight>clientHeight is unreliable cross-browser when pixel heights aren't integers. + // much more reliable to see if children are taller than the scroller, even tho doesn't account for + // inner-child margins and absolute positioning + var el = this.el; + var realClientHeight = this.el.getBoundingClientRect().height - this.getXScrollbarWidth(); + var children = el.children; + for (var i = 0; i < children.length; i++) { + var childEl = children[i]; + if (childEl.getBoundingClientRect().height > realClientHeight) { + return true; + } + } + return false; + }; + Scroller.prototype.getXScrollbarWidth = function () { + if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) { + return 0; + } + else { + return this.el.offsetHeight - this.el.clientHeight; // only works because we guarantee no borders. TODO: add to CSS with important? + } + }; + Scroller.prototype.getYScrollbarWidth = function () { + if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) { + return 0; + } + else { + return this.el.offsetWidth - this.el.clientWidth; // only works because we guarantee no borders. TODO: add to CSS with important? + } + }; + return Scroller; + }(BaseComponent)); + + /* + TODO: somehow infer OtherArgs from masterCallback? + TODO: infer RefType from masterCallback if provided + */ + var RefMap = /** @class */ (function () { + function RefMap(masterCallback) { + var _this = this; + this.masterCallback = masterCallback; + this.currentMap = {}; + this.depths = {}; + this.callbackMap = {}; + this.handleValue = function (val, key) { + var _a = _this, depths = _a.depths, currentMap = _a.currentMap; + var removed = false; + var added = false; + if (val !== null) { + removed = (key in currentMap); // for bug... ACTUALLY: can probably do away with this now that callers don't share numeric indices anymore + currentMap[key] = val; + depths[key] = (depths[key] || 0) + 1; + added = true; + } + else if (--depths[key] === 0) { + delete currentMap[key]; + delete _this.callbackMap[key]; + removed = true; + } + if (_this.masterCallback) { + if (removed) { + _this.masterCallback(null, String(key)); + } + if (added) { + _this.masterCallback(val, String(key)); + } + } + }; + } + RefMap.prototype.createRef = function (key) { + var _this = this; + var refCallback = this.callbackMap[key]; + if (!refCallback) { + refCallback = this.callbackMap[key] = function (val) { + _this.handleValue(val, String(key)); + }; + } + return refCallback; + }; + // TODO: check callers that don't care about order. should use getAll instead + // NOTE: this method has become less valuable now that we are encouraged to map order by some other index + // TODO: provide ONE array-export function, buildArray, which fails on non-numeric indexes. caller can manipulate and "collect" + RefMap.prototype.collect = function (startIndex, endIndex, step) { + return collectFromHash(this.currentMap, startIndex, endIndex, step); + }; + RefMap.prototype.getAll = function () { + return hashValuesToArray(this.currentMap); + }; + return RefMap; + }()); + + function computeShrinkWidth(chunkEls) { + var shrinkCells = findElements(chunkEls, '.fc-scrollgrid-shrink'); + var largestWidth = 0; + for (var _i = 0, shrinkCells_1 = shrinkCells; _i < shrinkCells_1.length; _i++) { + var shrinkCell = shrinkCells_1[_i]; + largestWidth = Math.max(largestWidth, computeSmallestCellWidth(shrinkCell)); + } + return Math.ceil(largestWidth); // elements work best with integers. round up to ensure contents fits + } + function getSectionHasLiquidHeight(props, sectionConfig) { + return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well) + } + function getAllowYScrolling(props, sectionConfig) { + return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars + getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars + } + // TODO: ONLY use `arg`. force out internal function to use same API + function renderChunkContent(sectionConfig, chunkConfig, arg) { + var expandRows = arg.expandRows; + var content = typeof chunkConfig.content === 'function' ? + chunkConfig.content(arg) : + createElement('table', { + className: [ + chunkConfig.tableClassName, + sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '' + ].join(' '), + style: { + minWidth: arg.tableMinWidth, + width: arg.clientWidth, + height: expandRows ? arg.clientHeight : '' // css `height` on a
serves as a min-height + } + }, arg.tableColGroupNode, createElement('tbody', {}, typeof chunkConfig.rowContent === 'function' ? chunkConfig.rowContent(arg) : chunkConfig.rowContent)); + return content; + } + function isColPropsEqual(cols0, cols1) { + return isArraysEqual(cols0, cols1, isPropsEqual); + } + function renderMicroColGroup(cols, shrinkWidth) { + var colNodes = []; + /* + for ColProps with spans, it would have been great to make a single + HOWEVER, Chrome was getting messing up distributing the width to elements makes Chrome behave. + */ + for (var _i = 0, cols_1 = cols; _i < cols_1.length; _i++) { + var colProps = cols_1[_i]; + var span = colProps.span || 1; + for (var i = 0; i < span; i++) { + colNodes.push(createElement("col", { style: { + width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''), + minWidth: colProps.minWidth || '' + } })); + } + } + return createElement.apply(void 0, __spreadArrays(['colgroup', {}], colNodes)); + } + function sanitizeShrinkWidth(shrinkWidth) { + /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth + 4 accounts for 2 2-pixel borders. TODO: better solution? */ + return shrinkWidth == null ? 4 : shrinkWidth; + } + function hasShrinkWidth(cols) { + for (var _i = 0, cols_2 = cols; _i < cols_2.length; _i++) { + var col = cols_2[_i]; + if (col.width === 'shrink') { + return true; + } + } + return false; + } + function getScrollGridClassNames(liquid, context) { + var classNames = [ + 'fc-scrollgrid', + context.theme.getClass('table') + ]; + if (liquid) { + classNames.push('fc-scrollgrid-liquid'); + } + return classNames; + } + function getSectionClassNames(sectionConfig, wholeTableVGrow) { + var classNames = [ + 'fc-scrollgrid-section', + 'fc-scrollgrid-section-' + sectionConfig.type, + sectionConfig.className // used? + ]; + if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) { + classNames.push('fc-scrollgrid-section-liquid'); + } + if (sectionConfig.isSticky) { + classNames.push('fc-scrollgrid-section-sticky'); + } + return classNames; + } + function renderScrollShim(arg) { + return (createElement("div", { className: 'fc-scrollgrid-sticky-shim', style: { + width: arg.clientWidth, + minWidth: arg.tableMinWidth + } })); + } + function getStickyHeaderDates(options) { + var stickyHeaderDates = options.stickyHeaderDates; + if (stickyHeaderDates == null || stickyHeaderDates === 'auto') { + stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto'; + } + return stickyHeaderDates; + } + function getStickyFooterScrollbar(options) { + var stickyFooterScrollbar = options.stickyFooterScrollbar; + if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') { + stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto'; + } + return stickyFooterScrollbar; + } + + var SimpleScrollGrid = /** @class */ (function (_super) { + __extends(SimpleScrollGrid, _super); + function SimpleScrollGrid() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.processCols = memoize(function (a) { return a; }, isColPropsEqual); // so we get same `cols` props every time + _this.renderMicroColGroup = memoize(renderMicroColGroup); // yucky to memoize VNodes, but much more efficient for consumers + _this.scrollerRefs = new RefMap(); + _this.scrollerElRefs = new RefMap(_this._handleScrollerEl.bind(_this)); + _this.state = { + shrinkWidth: null, + forceYScrollbars: false, + scrollerClientWidths: {}, + scrollerClientHeights: {} + }; + // TODO: can do a really simple print-view. dont need to join rows + _this.handleSizing = function () { + _this.setState(__assign({ shrinkWidth: _this.computeShrinkWidth() }, _this.computeScrollerDims())); + }; + return _this; + } + SimpleScrollGrid.prototype.render = function () { + var _a = this, props = _a.props, state = _a.state, context = _a.context; + var sectionConfigs = props.sections || []; + var cols = this.processCols(props.cols); + var microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth); + var classNames = getScrollGridClassNames(props.liquid, context); + // TODO: make DRY + var configCnt = sectionConfigs.length; + var configI = 0; + var currentConfig; + var headSectionNodes = []; + var bodySectionNodes = []; + var footSectionNodes = []; + while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') { + headSectionNodes.push(this.renderSection(currentConfig, configI, microColGroupNode)); + configI++; + } + while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') { + bodySectionNodes.push(this.renderSection(currentConfig, configI, microColGroupNode)); + configI++; + } + while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') { + footSectionNodes.push(this.renderSection(currentConfig, configI, microColGroupNode)); + configI++; + } + // firefox bug: when setting height on table and there is a thead or tfoot, + // the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524) + // use getCanVGrowWithinCell as a way to detect table-stupid firefox. + // if so, use a simpler dom structure, jam everything into a lone tbody. + var isBuggy = !getCanVGrowWithinCell(); + return createElement('table', { + className: classNames.join(' '), + style: { height: props.height } + }, Boolean(!isBuggy && headSectionNodes.length) && createElement.apply(void 0, __spreadArrays(['thead', {}], headSectionNodes)), Boolean(!isBuggy && bodySectionNodes.length) && createElement.apply(void 0, __spreadArrays(['tbody', {}], bodySectionNodes)), Boolean(!isBuggy && footSectionNodes.length) && createElement.apply(void 0, __spreadArrays(['tfoot', {}], footSectionNodes)), isBuggy && createElement.apply(void 0, __spreadArrays(['tbody', {}], headSectionNodes, bodySectionNodes, footSectionNodes))); + }; + SimpleScrollGrid.prototype.renderSection = function (sectionConfig, sectionI, microColGroupNode) { + if ('outerContent' in sectionConfig) { + return (createElement(Fragment, { key: sectionConfig.key }, sectionConfig.outerContent)); + } + return (createElement("tr", { key: sectionConfig.key, className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, sectionI, microColGroupNode, sectionConfig.chunk))); + }; + SimpleScrollGrid.prototype.renderChunkTd = function (sectionConfig, sectionI, microColGroupNode, chunkConfig) { + if ('outerContent' in chunkConfig) { + return chunkConfig.outerContent; + } + var props = this.props; + var _a = this.state, forceYScrollbars = _a.forceYScrollbars, scrollerClientWidths = _a.scrollerClientWidths, scrollerClientHeights = _a.scrollerClientHeights; + var needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config? + var isLiquid = getSectionHasLiquidHeight(props, sectionConfig); + // for `!props.liquid` - is WHOLE scrollgrid natural height? + // TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars + var overflowY = !props.liquid ? 'visible' : + forceYScrollbars ? 'scroll' : + !needsYScrolling ? 'hidden' : + 'auto'; + var content = renderChunkContent(sectionConfig, chunkConfig, { + tableColGroupNode: microColGroupNode, + tableMinWidth: '', + clientWidth: scrollerClientWidths[sectionI] !== undefined ? scrollerClientWidths[sectionI] : null, + clientHeight: scrollerClientHeights[sectionI] !== undefined ? scrollerClientHeights[sectionI] : null, + expandRows: sectionConfig.expandRows, + syncRowHeights: false, + rowSyncHeights: [], + reportRowHeightChange: function () { } + }); + return (createElement("td", { ref: chunkConfig.elRef }, + createElement("div", { className: 'fc-scroller-harness' + (isLiquid ? ' fc-scroller-harness-liquid' : '') }, + createElement(Scroller, { ref: this.scrollerRefs.createRef(sectionI), elRef: this.scrollerElRefs.createRef(sectionI), overflowY: overflowY, overflowX: !props.liquid ? 'visible' : 'hidden' /* natural height? */, maxHeight: sectionConfig.maxHeight, liquid: isLiquid, liquidIsAbsolute: true /* because its within a harness */ }, content)))); + }; + SimpleScrollGrid.prototype._handleScrollerEl = function (scrollerEl, key) { + var sectionI = parseInt(key, 10); + var chunkConfig = this.props.sections[sectionI].chunk; + setRef(chunkConfig.scrollerElRef, scrollerEl); + }; + SimpleScrollGrid.prototype.componentDidMount = function () { + this.handleSizing(); + this.context.addResizeHandler(this.handleSizing); + }; + SimpleScrollGrid.prototype.componentDidUpdate = function () { + // TODO: need better solution when state contains non-sizing things + this.handleSizing(); + }; + SimpleScrollGrid.prototype.componentWillUnmount = function () { + this.context.removeResizeHandler(this.handleSizing); + }; + SimpleScrollGrid.prototype.computeShrinkWidth = function () { + return hasShrinkWidth(this.props.cols) + ? computeShrinkWidth(this.scrollerElRefs.getAll()) + : 0; + }; + SimpleScrollGrid.prototype.computeScrollerDims = function () { + var scrollbarWidth = getScrollbarWidths(); + var sectionCnt = this.props.sections.length; + var _a = this, scrollerRefs = _a.scrollerRefs, scrollerElRefs = _a.scrollerElRefs; + var forceYScrollbars = false; + var scrollerClientWidths = {}; + var scrollerClientHeights = {}; + for (var sectionI = 0; sectionI < sectionCnt; sectionI++) { // along edge + var scroller = scrollerRefs.currentMap[sectionI]; + if (scroller && scroller.needsYScrolling()) { + forceYScrollbars = true; + break; + } + } + for (var sectionI = 0; sectionI < sectionCnt; sectionI++) { // along edge + var scrollerEl = scrollerElRefs.currentMap[sectionI]; + if (scrollerEl) { + var harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders + scrollerClientWidths[sectionI] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars + ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future + : 0)); + scrollerClientHeights[sectionI] = Math.floor(harnessEl.getBoundingClientRect().height // never has horizontal scrollbars + ); + } + } + return { forceYScrollbars: forceYScrollbars, scrollerClientWidths: scrollerClientWidths, scrollerClientHeights: scrollerClientHeights }; + }; + return SimpleScrollGrid; + }(BaseComponent)); + SimpleScrollGrid.addStateEquality({ + scrollerClientWidths: isPropsEqual, + scrollerClientHeights: isPropsEqual + }); + + var EventRoot = /** @class */ (function (_super) { + __extends(EventRoot, _super); + function EventRoot() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.elRef = createRef(); + return _this; + } + EventRoot.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var options = context.options; + var seg = props.seg; + var eventRange = seg.eventRange; + var ui = eventRange.ui; + var hookProps = { + event: new EventApi(context, eventRange.def, eventRange.instance), + view: context.viewApi, + timeText: props.timeText, + textColor: ui.textColor, + backgroundColor: ui.backgroundColor, + borderColor: ui.borderColor, + isDraggable: !props.disableDragging && computeSegDraggable(seg, context), + isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context), + isEndResizable: !props.disableResizing && computeSegEndResizable(seg), + isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting), + isStart: Boolean(seg.isStart), + isEnd: Boolean(seg.isEnd), + isPast: Boolean(props.isPast), + isFuture: Boolean(props.isFuture), + isToday: Boolean(props.isToday), + isSelected: Boolean(props.isSelected), + isDragging: Boolean(props.isDragging), + isResizing: Boolean(props.isResizing) + }; + var standardClassNames = getEventClassNames(hookProps).concat(ui.classNames); + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.eventClassNames, content: options.eventContent, defaultContent: props.defaultContent, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount, elRef: this.elRef }, function (rootElRef, customClassNames, innerElRef, innerContent) { return props.children(rootElRef, standardClassNames.concat(customClassNames), innerElRef, innerContent, hookProps); })); + }; + EventRoot.prototype.componentDidMount = function () { + setElSeg(this.elRef.current, this.props.seg); + }; + /* + need to re-assign seg to the element if seg changes, even if the element is the same + */ + EventRoot.prototype.componentDidUpdate = function (prevProps) { + var seg = this.props.seg; + if (seg !== prevProps.seg) { + setElSeg(this.elRef.current, seg); + } + }; + return EventRoot; + }(BaseComponent)); + + // should not be a purecomponent + var StandardEvent = /** @class */ (function (_super) { + __extends(StandardEvent, _super); + function StandardEvent() { + return _super !== null && _super.apply(this, arguments) || this; + } + StandardEvent.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var seg = props.seg; + var timeFormat = context.options.eventTimeFormat || props.defaultTimeFormat; + var timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd); + return (createElement(EventRoot, { seg: seg, timeText: timeText, disableDragging: props.disableDragging, disableResizing: props.disableResizing, defaultContent: props.defaultContent || renderInnerContent, isDragging: props.isDragging, isResizing: props.isResizing, isDateSelecting: props.isDateSelecting, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("a", __assign({ className: props.extraClassNames.concat(classNames).join(' '), style: { + borderColor: hookProps.borderColor, + backgroundColor: hookProps.backgroundColor + }, ref: rootElRef }, getSegAnchorAttrs(seg)), + createElement("div", { className: 'fc-event-main', ref: innerElRef, style: { color: hookProps.textColor } }, innerContent), + hookProps.isStartResizable && + createElement("div", { className: 'fc-event-resizer fc-event-resizer-start' }), + hookProps.isEndResizable && + createElement("div", { className: 'fc-event-resizer fc-event-resizer-end' }))); })); + }; + return StandardEvent; + }(BaseComponent)); + function renderInnerContent(innerProps) { + return (createElement("div", { className: 'fc-event-main-frame' }, + innerProps.timeText && + createElement("div", { className: 'fc-event-time' }, innerProps.timeText), + createElement("div", { className: 'fc-event-title-container' }, + createElement("div", { className: 'fc-event-title fc-sticky' }, innerProps.event.title || createElement(Fragment, null, "\u00A0"))))); + } + function getSegAnchorAttrs(seg) { + var url = seg.eventRange.def.url; + return url ? { href: url } : {}; + } + + var NowIndicatorRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) { + var options = context.options; + var hookProps = { + isAxis: props.isAxis, + date: context.dateEnv.toDate(props.date), + view: context.viewApi + }; + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.nowIndicatorClassNames, content: options.nowIndicatorContent, didMount: options.nowIndicatorDidMount, willUnmount: options.nowIndicatorWillUnmount }, props.children)); + })); }; + + var DAY_NUM_FORMAT = createFormatter({ day: 'numeric' }); + var DayCellRoot = /** @class */ (function (_super) { + __extends(DayCellRoot, _super); + function DayCellRoot() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.refineHookProps = memoizeObjArg(refineHookProps); + _this.normalizeClassNames = buildClassNameNormalizer(); + return _this; + } + DayCellRoot.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var options = context.options; + var hookProps = this.refineHookProps({ + date: props.date, + dateProfile: props.dateProfile, + todayRange: props.todayRange, + showDayNumber: props.showDayNumber, + extraProps: props.extraHookProps, + viewApi: context.viewApi, + dateEnv: context.dateEnv + }); + var classNames = getDayClassNames(hookProps, context.theme).concat(hookProps.isDisabled + ? [] // don't use custom classNames if disabled + : this.normalizeClassNames(options.dayCellClassNames, hookProps)); + var dataAttrs = hookProps.isDisabled ? {} : { + 'data-date': formatDayString(props.date) + }; + return (createElement(MountHook, { hookProps: hookProps, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount, elRef: props.elRef }, function (rootElRef) { return props.children(rootElRef, classNames, dataAttrs, hookProps.isDisabled); })); + }; + return DayCellRoot; + }(BaseComponent)); + var DayCellContent = /** @class */ (function (_super) { + __extends(DayCellContent, _super); + function DayCellContent() { + return _super !== null && _super.apply(this, arguments) || this; + } + DayCellContent.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var options = context.options; + var hookProps = refineHookProps({ + date: props.date, + dateProfile: props.dateProfile, + todayRange: props.todayRange, + showDayNumber: props.showDayNumber, + extraProps: props.extraHookProps, + viewApi: context.viewApi, + dateEnv: context.dateEnv + }); + return (createElement(ContentHook, { hookProps: hookProps, content: options.dayCellContent, defaultContent: props.defaultContent }, props.children)); + }; + return DayCellContent; + }(BaseComponent)); + function refineHookProps(raw) { + var date = raw.date, dateEnv = raw.dateEnv; + var dayMeta = getDateMeta(date, raw.todayRange, null, raw.dateProfile); + return __assign(__assign(__assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { dayNumberText: raw.showDayNumber ? dateEnv.format(date, DAY_NUM_FORMAT) : '' }), raw.extraProps); + } + + function renderFill(fillType) { + return (createElement("div", { className: "fc-" + fillType })); + } + var BgEvent = function (props) { return (createElement(EventRoot, { defaultContent: renderInnerContent$1, seg: props.seg /* uselesss i think */, timeText: '' /* weird */, disableDragging: true, disableResizing: true, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("div", { ref: rootElRef, className: ['fc-bg-event'].concat(classNames).join(' '), style: { + backgroundColor: hookProps.backgroundColor + } }, innerContent)); })); }; + function renderInnerContent$1(props) { + var title = props.event.title; + return title && (createElement("div", { className: 'fc-event-title' }, props.event.title)); + } + + var WeekNumberRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) { + var dateEnv = context.dateEnv, options = context.options; + var date = props.date; + var format = options.weekNumberFormat || props.defaultFormat; + var num = dateEnv.computeWeekNumber(date); // TODO: somehow use for formatting as well? + var text = dateEnv.format(date, format); + var hookProps = { num: num, text: text, date: date }; + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.weekNumberClassNames, content: options.weekNumberContent, defaultContent: renderInner$1, didMount: options.weekNumberDidMount, willUnmount: options.weekNumberWillUnmount }, props.children)); + })); }; + function renderInner$1(innerProps) { + return innerProps.text; + } + + // exports + // -------------------------------------------------------------------------------------------------- + var version = '5.3.2'; // important to type it, so .d.ts has generic string + + var Calendar = /** @class */ (function (_super) { + __extends(Calendar, _super); + function Calendar(el, optionOverrides) { + if (optionOverrides === void 0) { optionOverrides = {}; } + var _this = _super.call(this) || this; + _this.isRendering = false; + _this.isRendered = false; + _this.currentClassNames = []; + _this.customContentRenderId = 0; // will affect custom generated classNames? + _this.handleAction = function (action) { + // actions we know we want to render immediately + switch (action.type) { + case 'SET_EVENT_DRAG': + case 'SET_EVENT_RESIZE': + _this.renderRunner.tryDrain(); + } + }; + _this.handleData = function (data) { + _this.currentData = data; + _this.renderRunner.request(data.calendarOptions.rerenderDelay); + }; + _this.handleRenderRequest = function () { + if (_this.isRendering) { + _this.isRendered = true; + var currentData_1 = _this.currentData; + render(createElement(CalendarRoot, { options: currentData_1.calendarOptions, theme: currentData_1.theme, emitter: currentData_1.emitter }, function (classNames, height, isHeightAuto, forPrint) { + _this.setClassNames(classNames); + _this.setHeight(height); + return (createElement(CustomContentRenderContext.Provider, { value: _this.customContentRenderId }, + createElement(CalendarContent, __assign({ isHeightAuto: isHeightAuto, forPrint: forPrint }, currentData_1)))); + }), _this.el); + } + else if (_this.isRendered) { + _this.isRendered = false; + render(null, _this.el); + _this.setClassNames([]); + _this.setHeight(''); + } + flushToDom$1(); + }; + _this.el = el; + _this.renderRunner = new DelayedRunner(_this.handleRenderRequest); + new CalendarDataManager({ + optionOverrides: optionOverrides, + calendarApi: _this, + onAction: _this.handleAction, + onData: _this.handleData + }); + return _this; + } + Object.defineProperty(Calendar.prototype, "view", { + get: function () { return this.currentData.viewApi; } // for public API + , + enumerable: false, + configurable: true + }); + Calendar.prototype.render = function () { + var wasRendering = this.isRendering; + if (!wasRendering) { + this.isRendering = true; + } + else { + this.customContentRenderId++; + } + this.renderRunner.request(); + if (wasRendering) { + this.updateSize(); + } + }; + Calendar.prototype.destroy = function () { + if (this.isRendering) { + this.isRendering = false; + this.renderRunner.request(); + } + }; + Calendar.prototype.updateSize = function () { + _super.prototype.updateSize.call(this); + flushToDom$1(); + }; + Calendar.prototype.batchRendering = function (func) { + this.renderRunner.pause('batchRendering'); + func(); + this.renderRunner.resume('batchRendering'); + }; + Calendar.prototype.pauseRendering = function () { + this.renderRunner.pause('pauseRendering'); + }; + Calendar.prototype.resumeRendering = function () { + this.renderRunner.resume('pauseRendering', true); + }; + Calendar.prototype.resetOptions = function (optionOverrides, append) { + this.currentDataManager.resetOptions(optionOverrides, append); + }; + Calendar.prototype.setClassNames = function (classNames) { + if (!isArraysEqual(classNames, this.currentClassNames)) { + var classList = this.el.classList; + for (var _i = 0, _a = this.currentClassNames; _i < _a.length; _i++) { + var className = _a[_i]; + classList.remove(className); + } + for (var _b = 0, classNames_1 = classNames; _b < classNames_1.length; _b++) { + var className = classNames_1[_b]; + classList.add(className); + } + this.currentClassNames = classNames; + } + }; + Calendar.prototype.setHeight = function (height) { + applyStyleProp(this.el, 'height', height); + }; + return Calendar; + }(CalendarApi)); + + config.touchMouseIgnoreWait = 500; + var ignoreMouseDepth = 0; + var listenerCnt = 0; + var isWindowTouchMoveCancelled = false; + /* + Uses a "pointer" abstraction, which monitors UI events for both mouse and touch. + Tracks when the pointer "drags" on a certain element, meaning down+move+up. + + Also, tracks if there was touch-scrolling. + Also, can prevent touch-scrolling from happening. + Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement. + + emits: + - pointerdown + - pointermove + - pointerup + */ + var PointerDragging = /** @class */ (function () { + function PointerDragging(containerEl) { + var _this = this; + this.subjectEl = null; + // options that can be directly assigned by caller + this.selector = ''; // will cause subjectEl in all emitted events to be this element + this.handleSelector = ''; + this.shouldIgnoreMove = false; + this.shouldWatchScroll = true; // for simulating pointermove on scroll + // internal states + this.isDragging = false; + this.isTouchDragging = false; + this.wasTouchScroll = false; + // Mouse + // ---------------------------------------------------------------------------------------------------- + this.handleMouseDown = function (ev) { + if (!_this.shouldIgnoreMouse() && + isPrimaryMouseButton(ev) && + _this.tryStart(ev)) { + var pev = _this.createEventFromMouse(ev, true); + _this.emitter.trigger('pointerdown', pev); + _this.initScrollWatch(pev); + if (!_this.shouldIgnoreMove) { + document.addEventListener('mousemove', _this.handleMouseMove); + } + document.addEventListener('mouseup', _this.handleMouseUp); + } + }; + this.handleMouseMove = function (ev) { + var pev = _this.createEventFromMouse(ev); + _this.recordCoords(pev); + _this.emitter.trigger('pointermove', pev); + }; + this.handleMouseUp = function (ev) { + document.removeEventListener('mousemove', _this.handleMouseMove); + document.removeEventListener('mouseup', _this.handleMouseUp); + _this.emitter.trigger('pointerup', _this.createEventFromMouse(ev)); + _this.cleanup(); // call last so that pointerup has access to props + }; + // Touch + // ---------------------------------------------------------------------------------------------------- + this.handleTouchStart = function (ev) { + if (_this.tryStart(ev)) { + _this.isTouchDragging = true; + var pev = _this.createEventFromTouch(ev, true); + _this.emitter.trigger('pointerdown', pev); + _this.initScrollWatch(pev); + // unlike mouse, need to attach to target, not document + // https://stackoverflow.com/a/45760014 + var targetEl = ev.target; + if (!_this.shouldIgnoreMove) { + targetEl.addEventListener('touchmove', _this.handleTouchMove); + } + targetEl.addEventListener('touchend', _this.handleTouchEnd); + targetEl.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end + // attach a handler to get called when ANY scroll action happens on the page. + // this was impossible to do with normal on/off because 'scroll' doesn't bubble. + // http://stackoverflow.com/a/32954565/96342 + window.addEventListener('scroll', _this.handleTouchScroll, true // useCapture + ); + } + }; + this.handleTouchMove = function (ev) { + var pev = _this.createEventFromTouch(ev); + _this.recordCoords(pev); + _this.emitter.trigger('pointermove', pev); + }; + this.handleTouchEnd = function (ev) { + if (_this.isDragging) { // done to guard against touchend followed by touchcancel + var targetEl = ev.target; + targetEl.removeEventListener('touchmove', _this.handleTouchMove); + targetEl.removeEventListener('touchend', _this.handleTouchEnd); + targetEl.removeEventListener('touchcancel', _this.handleTouchEnd); + window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true + _this.emitter.trigger('pointerup', _this.createEventFromTouch(ev)); + _this.cleanup(); // call last so that pointerup has access to props + _this.isTouchDragging = false; + startIgnoringMouse(); + } + }; + this.handleTouchScroll = function () { + _this.wasTouchScroll = true; + }; + this.handleScroll = function (ev) { + if (!_this.shouldIgnoreMove) { + var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX; + var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY; + _this.emitter.trigger('pointermove', { + origEvent: ev, + isTouch: _this.isTouchDragging, + subjectEl: _this.subjectEl, + pageX: pageX, + pageY: pageY, + deltaX: pageX - _this.origPageX, + deltaY: pageY - _this.origPageY + }); + } + }; + this.containerEl = containerEl; + this.emitter = new Emitter(); + containerEl.addEventListener('mousedown', this.handleMouseDown); + containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true }); + listenerCreated(); + } + PointerDragging.prototype.destroy = function () { + this.containerEl.removeEventListener('mousedown', this.handleMouseDown); + this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true }); + listenerDestroyed(); + }; + PointerDragging.prototype.tryStart = function (ev) { + var subjectEl = this.querySubjectEl(ev); + var downEl = ev.target; + if (subjectEl && + (!this.handleSelector || elementClosest(downEl, this.handleSelector))) { + this.subjectEl = subjectEl; + this.isDragging = true; // do this first so cancelTouchScroll will work + this.wasTouchScroll = false; + return true; + } + return false; + }; + PointerDragging.prototype.cleanup = function () { + isWindowTouchMoveCancelled = false; + this.isDragging = false; + this.subjectEl = null; + // keep wasTouchScroll around for later access + this.destroyScrollWatch(); + }; + PointerDragging.prototype.querySubjectEl = function (ev) { + if (this.selector) { + return elementClosest(ev.target, this.selector); + } + else { + return this.containerEl; + } + }; + PointerDragging.prototype.shouldIgnoreMouse = function () { + return ignoreMouseDepth || this.isTouchDragging; + }; + // can be called by user of this class, to cancel touch-based scrolling for the current drag + PointerDragging.prototype.cancelTouchScroll = function () { + if (this.isDragging) { + isWindowTouchMoveCancelled = true; + } + }; + // Scrolling that simulates pointermoves + // ---------------------------------------------------------------------------------------------------- + PointerDragging.prototype.initScrollWatch = function (ev) { + if (this.shouldWatchScroll) { + this.recordCoords(ev); + window.addEventListener('scroll', this.handleScroll, true); // useCapture=true + } + }; + PointerDragging.prototype.recordCoords = function (ev) { + if (this.shouldWatchScroll) { + this.prevPageX = ev.pageX; + this.prevPageY = ev.pageY; + this.prevScrollX = window.pageXOffset; + this.prevScrollY = window.pageYOffset; + } + }; + PointerDragging.prototype.destroyScrollWatch = function () { + if (this.shouldWatchScroll) { + window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true + } + }; + // Event Normalization + // ---------------------------------------------------------------------------------------------------- + PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) { + var deltaX = 0; + var deltaY = 0; + // TODO: repeat code + if (isFirst) { + this.origPageX = ev.pageX; + this.origPageY = ev.pageY; + } + else { + deltaX = ev.pageX - this.origPageX; + deltaY = ev.pageY - this.origPageY; + } + return { + origEvent: ev, + isTouch: false, + subjectEl: this.subjectEl, + pageX: ev.pageX, + pageY: ev.pageY, + deltaX: deltaX, + deltaY: deltaY + }; + }; + PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) { + var touches = ev.touches; + var pageX; + var pageY; + var deltaX = 0; + var deltaY = 0; + // if touch coords available, prefer, + // because FF would give bad ev.pageX ev.pageY + if (touches && touches.length) { + pageX = touches[0].pageX; + pageY = touches[0].pageY; + } + else { + pageX = ev.pageX; + pageY = ev.pageY; + } + // TODO: repeat code + if (isFirst) { + this.origPageX = pageX; + this.origPageY = pageY; + } + else { + deltaX = pageX - this.origPageX; + deltaY = pageY - this.origPageY; + } + return { + origEvent: ev, + isTouch: true, + subjectEl: this.subjectEl, + pageX: pageX, + pageY: pageY, + deltaX: deltaX, + deltaY: deltaY + }; + }; + return PointerDragging; + }()); + // Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac) + function isPrimaryMouseButton(ev) { + return ev.button === 0 && !ev.ctrlKey; + } + // Ignoring fake mouse events generated by touch + // ---------------------------------------------------------------------------------------------------- + function startIgnoringMouse() { + ignoreMouseDepth++; + setTimeout(function () { + ignoreMouseDepth--; + }, config.touchMouseIgnoreWait); + } + // We want to attach touchmove as early as possible for Safari + // ---------------------------------------------------------------------------------------------------- + function listenerCreated() { + if (!(listenerCnt++)) { + window.addEventListener('touchmove', onWindowTouchMove, { passive: false }); + } + } + function listenerDestroyed() { + if (!(--listenerCnt)) { + window.removeEventListener('touchmove', onWindowTouchMove, { passive: false }); + } + } + function onWindowTouchMove(ev) { + if (isWindowTouchMoveCancelled) { + ev.preventDefault(); + } + } + + /* + An effect in which an element follows the movement of a pointer across the screen. + The moving element is a clone of some other element. + Must call start + handleMove + stop. + */ + var ElementMirror = /** @class */ (function () { + function ElementMirror() { + this.isVisible = false; // must be explicitly enabled + this.sourceEl = null; + this.mirrorEl = null; + this.sourceElRect = null; // screen coords relative to viewport + // options that can be set directly by caller + this.parentNode = document.body; + this.zIndex = 9999; + this.revertDuration = 0; + } + ElementMirror.prototype.start = function (sourceEl, pageX, pageY) { + this.sourceEl = sourceEl; + this.sourceElRect = this.sourceEl.getBoundingClientRect(); + this.origScreenX = pageX - window.pageXOffset; + this.origScreenY = pageY - window.pageYOffset; + this.deltaX = 0; + this.deltaY = 0; + this.updateElPosition(); + }; + ElementMirror.prototype.handleMove = function (pageX, pageY) { + this.deltaX = (pageX - window.pageXOffset) - this.origScreenX; + this.deltaY = (pageY - window.pageYOffset) - this.origScreenY; + this.updateElPosition(); + }; + // can be called before start + ElementMirror.prototype.setIsVisible = function (bool) { + if (bool) { + if (!this.isVisible) { + if (this.mirrorEl) { + this.mirrorEl.style.display = ''; + } + this.isVisible = bool; // needs to happen before updateElPosition + this.updateElPosition(); // because was not updating the position while invisible + } + } + else { + if (this.isVisible) { + if (this.mirrorEl) { + this.mirrorEl.style.display = 'none'; + } + this.isVisible = bool; + } + } + }; + // always async + ElementMirror.prototype.stop = function (needsRevertAnimation, callback) { + var _this = this; + var done = function () { + _this.cleanup(); + callback(); + }; + if (needsRevertAnimation && + this.mirrorEl && + this.isVisible && + this.revertDuration && // if 0, transition won't work + (this.deltaX || this.deltaY) // if same coords, transition won't work + ) { + this.doRevertAnimation(done, this.revertDuration); + } + else { + setTimeout(done, 0); + } + }; + ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) { + var mirrorEl = this.mirrorEl; + var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened + mirrorEl.style.transition = + 'top ' + revertDuration + 'ms,' + + 'left ' + revertDuration + 'ms'; + applyStyle(mirrorEl, { + left: finalSourceElRect.left, + top: finalSourceElRect.top + }); + whenTransitionDone(mirrorEl, function () { + mirrorEl.style.transition = ''; + callback(); + }); + }; + ElementMirror.prototype.cleanup = function () { + if (this.mirrorEl) { + removeElement(this.mirrorEl); + this.mirrorEl = null; + } + this.sourceEl = null; + }; + ElementMirror.prototype.updateElPosition = function () { + if (this.sourceEl && this.isVisible) { + applyStyle(this.getMirrorEl(), { + left: this.sourceElRect.left + this.deltaX, + top: this.sourceElRect.top + this.deltaY + }); + } + }; + ElementMirror.prototype.getMirrorEl = function () { + var sourceElRect = this.sourceElRect; + var mirrorEl = this.mirrorEl; + if (!mirrorEl) { + mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true + // we don't want long taps or any mouse interaction causing selection/menus. + // would use preventSelection(), but that prevents selectstart, causing problems. + mirrorEl.classList.add('fc-unselectable'); + mirrorEl.classList.add('fc-event-dragging'); + applyStyle(mirrorEl, { + position: 'fixed', + zIndex: this.zIndex, + visibility: '', + boxSizing: 'border-box', + width: sourceElRect.right - sourceElRect.left, + height: sourceElRect.bottom - sourceElRect.top, + right: 'auto', + bottom: 'auto', + margin: 0 + }); + this.parentNode.appendChild(mirrorEl); + } + return mirrorEl; + }; + return ElementMirror; + }()); + + /* + Is a cache for a given element's scroll information (all the info that ScrollController stores) + in addition the "client rectangle" of the element.. the area within the scrollbars. + + The cache can be in one of two modes: + - doesListening:false - ignores when the container is scrolled by someone else + - doesListening:true - watch for scrolling and update the cache + */ + var ScrollGeomCache = /** @class */ (function (_super) { + __extends(ScrollGeomCache, _super); + function ScrollGeomCache(scrollController, doesListening) { + var _this = _super.call(this) || this; + _this.handleScroll = function () { + _this.scrollTop = _this.scrollController.getScrollTop(); + _this.scrollLeft = _this.scrollController.getScrollLeft(); + _this.handleScrollChange(); + }; + _this.scrollController = scrollController; + _this.doesListening = doesListening; + _this.scrollTop = _this.origScrollTop = scrollController.getScrollTop(); + _this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft(); + _this.scrollWidth = scrollController.getScrollWidth(); + _this.scrollHeight = scrollController.getScrollHeight(); + _this.clientWidth = scrollController.getClientWidth(); + _this.clientHeight = scrollController.getClientHeight(); + _this.clientRect = _this.computeClientRect(); // do last in case it needs cached values + if (_this.doesListening) { + _this.getEventTarget().addEventListener('scroll', _this.handleScroll); + } + return _this; + } + ScrollGeomCache.prototype.destroy = function () { + if (this.doesListening) { + this.getEventTarget().removeEventListener('scroll', this.handleScroll); + } + }; + ScrollGeomCache.prototype.getScrollTop = function () { + return this.scrollTop; + }; + ScrollGeomCache.prototype.getScrollLeft = function () { + return this.scrollLeft; + }; + ScrollGeomCache.prototype.setScrollTop = function (top) { + this.scrollController.setScrollTop(top); + if (!this.doesListening) { + // we are not relying on the element to normalize out-of-bounds scroll values + // so we need to sanitize ourselves + this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0); + this.handleScrollChange(); + } + }; + ScrollGeomCache.prototype.setScrollLeft = function (top) { + this.scrollController.setScrollLeft(top); + if (!this.doesListening) { + // we are not relying on the element to normalize out-of-bounds scroll values + // so we need to sanitize ourselves + this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0); + this.handleScrollChange(); + } + }; + ScrollGeomCache.prototype.getClientWidth = function () { + return this.clientWidth; + }; + ScrollGeomCache.prototype.getClientHeight = function () { + return this.clientHeight; + }; + ScrollGeomCache.prototype.getScrollWidth = function () { + return this.scrollWidth; + }; + ScrollGeomCache.prototype.getScrollHeight = function () { + return this.scrollHeight; + }; + ScrollGeomCache.prototype.handleScrollChange = function () { + }; + return ScrollGeomCache; + }(ScrollController)); + var ElementScrollGeomCache = /** @class */ (function (_super) { + __extends(ElementScrollGeomCache, _super); + function ElementScrollGeomCache(el, doesListening) { + return _super.call(this, new ElementScrollController(el), doesListening) || this; + } + ElementScrollGeomCache.prototype.getEventTarget = function () { + return this.scrollController.el; + }; + ElementScrollGeomCache.prototype.computeClientRect = function () { + return computeInnerRect(this.scrollController.el); + }; + return ElementScrollGeomCache; + }(ScrollGeomCache)); + var WindowScrollGeomCache = /** @class */ (function (_super) { + __extends(WindowScrollGeomCache, _super); + function WindowScrollGeomCache(doesListening) { + return _super.call(this, new WindowScrollController(), doesListening) || this; + } + WindowScrollGeomCache.prototype.getEventTarget = function () { + return window; + }; + WindowScrollGeomCache.prototype.computeClientRect = function () { + return { + left: this.scrollLeft, + right: this.scrollLeft + this.clientWidth, + top: this.scrollTop, + bottom: this.scrollTop + this.clientHeight + }; + }; + // the window is the only scroll object that changes it's rectangle relative + // to the document's topleft as it scrolls + WindowScrollGeomCache.prototype.handleScrollChange = function () { + this.clientRect = this.computeClientRect(); + }; + return WindowScrollGeomCache; + }(ScrollGeomCache)); + + // If available we are using native "performance" API instead of "Date" + // Read more about it on MDN: + // https://developer.mozilla.org/en-US/docs/Web/API/Performance + var getTime = typeof performance === 'function' ? performance.now : Date.now; + /* + For a pointer interaction, automatically scrolls certain scroll containers when the pointer + approaches the edge. + + The caller must call start + handleMove + stop. + */ + var AutoScroller = /** @class */ (function () { + function AutoScroller() { + var _this = this; + // options that can be set by caller + this.isEnabled = true; + this.scrollQuery = [window, '.fc-scroller']; + this.edgeThreshold = 50; // pixels + this.maxVelocity = 300; // pixels per second + // internal state + this.pointerScreenX = null; + this.pointerScreenY = null; + this.isAnimating = false; + this.scrollCaches = null; + // protect against the initial pointerdown being too close to an edge and starting the scroll + this.everMovedUp = false; + this.everMovedDown = false; + this.everMovedLeft = false; + this.everMovedRight = false; + this.animate = function () { + if (_this.isAnimating) { // wasn't cancelled between animation calls + var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset); + if (edge) { + var now = getTime(); + _this.handleSide(edge, (now - _this.msSinceRequest) / 1000); + _this.requestAnimation(now); + } + else { + _this.isAnimating = false; // will stop animation + } + } + }; + } + AutoScroller.prototype.start = function (pageX, pageY) { + if (this.isEnabled) { + this.scrollCaches = this.buildCaches(); + this.pointerScreenX = null; + this.pointerScreenY = null; + this.everMovedUp = false; + this.everMovedDown = false; + this.everMovedLeft = false; + this.everMovedRight = false; + this.handleMove(pageX, pageY); + } + }; + AutoScroller.prototype.handleMove = function (pageX, pageY) { + if (this.isEnabled) { + var pointerScreenX = pageX - window.pageXOffset; + var pointerScreenY = pageY - window.pageYOffset; + var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY; + var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX; + if (yDelta < 0) { + this.everMovedUp = true; + } + else if (yDelta > 0) { + this.everMovedDown = true; + } + if (xDelta < 0) { + this.everMovedLeft = true; + } + else if (xDelta > 0) { + this.everMovedRight = true; + } + this.pointerScreenX = pointerScreenX; + this.pointerScreenY = pointerScreenY; + if (!this.isAnimating) { + this.isAnimating = true; + this.requestAnimation(getTime()); + } + } + }; + AutoScroller.prototype.stop = function () { + if (this.isEnabled) { + this.isAnimating = false; // will stop animation + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + scrollCache.destroy(); + } + this.scrollCaches = null; + } + }; + AutoScroller.prototype.requestAnimation = function (now) { + this.msSinceRequest = now; + requestAnimationFrame(this.animate); + }; + AutoScroller.prototype.handleSide = function (edge, seconds) { + var scrollCache = edge.scrollCache; + var edgeThreshold = this.edgeThreshold; + var invDistance = edgeThreshold - edge.distance; + var velocity = // the closer to the edge, the faster we scroll + (invDistance * invDistance) / (edgeThreshold * edgeThreshold) * // quadratic + this.maxVelocity * seconds; + var sign = 1; + switch (edge.name) { + case 'left': + sign = -1; + // falls through + case 'right': + scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign); + break; + case 'top': + sign = -1; + // falls through + case 'bottom': + scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign); + break; + } + }; + // left/top are relative to document topleft + AutoScroller.prototype.computeBestEdge = function (left, top) { + var edgeThreshold = this.edgeThreshold; + var bestSide = null; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + var rect = scrollCache.clientRect; + var leftDist = left - rect.left; + var rightDist = rect.right - left; + var topDist = top - rect.top; + var bottomDist = rect.bottom - top; + // completely within the rect? + if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) { + if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() && + (!bestSide || bestSide.distance > topDist)) { + bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist }; + } + if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() && + (!bestSide || bestSide.distance > bottomDist)) { + bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist }; + } + if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() && + (!bestSide || bestSide.distance > leftDist)) { + bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist }; + } + if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() && + (!bestSide || bestSide.distance > rightDist)) { + bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist }; + } + } + } + return bestSide; + }; + AutoScroller.prototype.buildCaches = function () { + return this.queryScrollEls().map(function (el) { + if (el === window) { + return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls + } + else { + return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls + } + }); + }; + AutoScroller.prototype.queryScrollEls = function () { + var els = []; + for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) { + var query = _a[_i]; + if (typeof query === 'object') { + els.push(query); + } + else { + els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query))); + } + } + return els; + }; + return AutoScroller; + }()); + + /* + Monitors dragging on an element. Has a number of high-level features: + - minimum distance required before dragging + - minimum wait time ("delay") before dragging + - a mirror element that follows the pointer + */ + var FeaturefulElementDragging = /** @class */ (function (_super) { + __extends(FeaturefulElementDragging, _super); + function FeaturefulElementDragging(containerEl, selector) { + var _this = _super.call(this, containerEl) || this; + // options that can be directly set by caller + // the caller can also set the PointerDragging's options as well + _this.delay = null; + _this.minDistance = 0; + _this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag + _this.mirrorNeedsRevert = false; + _this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup + _this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation + _this.isDelayEnded = false; + _this.isDistanceSurpassed = false; + _this.delayTimeoutId = null; + _this.onPointerDown = function (ev) { + if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going + _this.isInteracting = true; + _this.isDelayEnded = false; + _this.isDistanceSurpassed = false; + preventSelection(document.body); + preventContextMenu(document.body); + // prevent links from being visited if there's an eventual drag. + // also prevents selection in older browsers (maybe?). + // not necessary for touch, besides, browser would complain about passiveness. + if (!ev.isTouch) { + ev.origEvent.preventDefault(); + } + _this.emitter.trigger('pointerdown', ev); + if (_this.isInteracting && // not destroyed via pointerdown handler + !_this.pointer.shouldIgnoreMove) { + // actions related to initiating dragstart+dragmove+dragend... + _this.mirror.setIsVisible(false); // reset. caller must set-visible + _this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down + _this.startDelay(ev); + if (!_this.minDistance) { + _this.handleDistanceSurpassed(ev); + } + } + } + }; + _this.onPointerMove = function (ev) { + if (_this.isInteracting) { + _this.emitter.trigger('pointermove', ev); + if (!_this.isDistanceSurpassed) { + var minDistance = _this.minDistance; + var distanceSq = void 0; // current distance from the origin, squared + var deltaX = ev.deltaX, deltaY = ev.deltaY; + distanceSq = deltaX * deltaX + deltaY * deltaY; + if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem + _this.handleDistanceSurpassed(ev); + } + } + if (_this.isDragging) { + // a real pointer move? (not one simulated by scrolling) + if (ev.origEvent.type !== 'scroll') { + _this.mirror.handleMove(ev.pageX, ev.pageY); + _this.autoScroller.handleMove(ev.pageX, ev.pageY); + } + _this.emitter.trigger('dragmove', ev); + } + } + }; + _this.onPointerUp = function (ev) { + if (_this.isInteracting) { + _this.isInteracting = false; + allowSelection(document.body); + allowContextMenu(document.body); + _this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert + if (_this.isDragging) { + _this.autoScroller.stop(); + _this.tryStopDrag(ev); // which will stop the mirror + } + if (_this.delayTimeoutId) { + clearTimeout(_this.delayTimeoutId); + _this.delayTimeoutId = null; + } + } + }; + var pointer = _this.pointer = new PointerDragging(containerEl); + pointer.emitter.on('pointerdown', _this.onPointerDown); + pointer.emitter.on('pointermove', _this.onPointerMove); + pointer.emitter.on('pointerup', _this.onPointerUp); + if (selector) { + pointer.selector = selector; + } + _this.mirror = new ElementMirror(); + _this.autoScroller = new AutoScroller(); + return _this; + } + FeaturefulElementDragging.prototype.destroy = function () { + this.pointer.destroy(); + // HACK: simulate a pointer-up to end the current drag + // TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire) + this.onPointerUp({}); + }; + FeaturefulElementDragging.prototype.startDelay = function (ev) { + var _this = this; + if (typeof this.delay === 'number') { + this.delayTimeoutId = setTimeout(function () { + _this.delayTimeoutId = null; + _this.handleDelayEnd(ev); + }, this.delay); // not assignable to number! + } + else { + this.handleDelayEnd(ev); + } + }; + FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) { + this.isDelayEnded = true; + this.tryStartDrag(ev); + }; + FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) { + this.isDistanceSurpassed = true; + this.tryStartDrag(ev); + }; + FeaturefulElementDragging.prototype.tryStartDrag = function (ev) { + if (this.isDelayEnded && this.isDistanceSurpassed) { + if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) { + this.isDragging = true; + this.mirrorNeedsRevert = false; + this.autoScroller.start(ev.pageX, ev.pageY); + this.emitter.trigger('dragstart', ev); + if (this.touchScrollAllowed === false) { + this.pointer.cancelTouchScroll(); + } + } + } + }; + FeaturefulElementDragging.prototype.tryStopDrag = function (ev) { + // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events + // that come from the document to fire beforehand. much more convenient this way. + this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev) // bound with args + ); + }; + FeaturefulElementDragging.prototype.stopDrag = function (ev) { + this.isDragging = false; + this.emitter.trigger('dragend', ev); + }; + // fill in the implementations... + FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) { + this.pointer.shouldIgnoreMove = bool; + }; + FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) { + this.mirror.setIsVisible(bool); + }; + FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) { + this.mirrorNeedsRevert = bool; + }; + FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) { + this.autoScroller.isEnabled = bool; + }; + return FeaturefulElementDragging; + }(ElementDragging)); + + /* + When this class is instantiated, it records the offset of an element (relative to the document topleft), + and continues to monitor scrolling, updating the cached coordinates if it needs to. + Does not access the DOM after instantiation, so highly performant. + + Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element + and an determine if a given point is inside the combined clipping rectangle. + */ + var OffsetTracker = /** @class */ (function () { + function OffsetTracker(el) { + this.origRect = computeRect(el); + // will work fine for divs that have overflow:hidden + this.scrollCaches = getClippingParents(el).map(function (el) { + return new ElementScrollGeomCache(el, true); // listen=true + }); + } + OffsetTracker.prototype.destroy = function () { + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + scrollCache.destroy(); + } + }; + OffsetTracker.prototype.computeLeft = function () { + var left = this.origRect.left; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + left += scrollCache.origScrollLeft - scrollCache.getScrollLeft(); + } + return left; + }; + OffsetTracker.prototype.computeTop = function () { + var top = this.origRect.top; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + top += scrollCache.origScrollTop - scrollCache.getScrollTop(); + } + return top; + }; + OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) { + var point = { left: pageX, top: pageY }; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + if (!isIgnoredClipping(scrollCache.getEventTarget()) && + !pointInsideRect(point, scrollCache.clientRect)) { + return false; + } + } + return true; + }; + return OffsetTracker; + }()); + // certain clipping containers should never constrain interactions, like and + // https://github.com/fullcalendar/fullcalendar/issues/3615 + function isIgnoredClipping(node) { + var tagName = node.tagName; + return tagName === 'HTML' || tagName === 'BODY'; + } + + /* + Tracks movement over multiple droppable areas (aka "hits") + that exist in one or more DateComponents. + Relies on an existing draggable. + + emits: + - pointerdown + - dragstart + - hitchange - fires initially, even if not over a hit + - pointerup + - (hitchange - again, to null, if ended over a hit) + - dragend + */ + var HitDragging = /** @class */ (function () { + function HitDragging(dragging, droppableStore) { + var _this = this; + // options that can be set by caller + this.useSubjectCenter = false; + this.requireInitial = true; // if doesn't start out on a hit, won't emit any events + this.initialHit = null; + this.movingHit = null; + this.finalHit = null; // won't ever be populated if shouldIgnoreMove + this.handlePointerDown = function (ev) { + var dragging = _this.dragging; + _this.initialHit = null; + _this.movingHit = null; + _this.finalHit = null; + _this.prepareHits(); + _this.processFirstCoord(ev); + if (_this.initialHit || !_this.requireInitial) { + dragging.setIgnoreMove(false); + _this.emitter.trigger('pointerdown', ev); // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :( + } + else { + dragging.setIgnoreMove(true); + } + }; + this.handleDragStart = function (ev) { + _this.emitter.trigger('dragstart', ev); + _this.handleMove(ev, true); // force = fire even if initially null + }; + this.handleDragMove = function (ev) { + _this.emitter.trigger('dragmove', ev); + _this.handleMove(ev); + }; + this.handlePointerUp = function (ev) { + _this.releaseHits(); + _this.emitter.trigger('pointerup', ev); + }; + this.handleDragEnd = function (ev) { + if (_this.movingHit) { + _this.emitter.trigger('hitupdate', null, true, ev); + } + _this.finalHit = _this.movingHit; + _this.movingHit = null; + _this.emitter.trigger('dragend', ev); + }; + this.droppableStore = droppableStore; + dragging.emitter.on('pointerdown', this.handlePointerDown); + dragging.emitter.on('dragstart', this.handleDragStart); + dragging.emitter.on('dragmove', this.handleDragMove); + dragging.emitter.on('pointerup', this.handlePointerUp); + dragging.emitter.on('dragend', this.handleDragEnd); + this.dragging = dragging; + this.emitter = new Emitter(); + } + // sets initialHit + // sets coordAdjust + HitDragging.prototype.processFirstCoord = function (ev) { + var origPoint = { left: ev.pageX, top: ev.pageY }; + var adjustedPoint = origPoint; + var subjectEl = ev.subjectEl; + var subjectRect; + if (subjectEl !== document) { + subjectRect = computeRect(subjectEl); + adjustedPoint = constrainPoint(adjustedPoint, subjectRect); + } + var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top); + if (initialHit) { + if (this.useSubjectCenter && subjectRect) { + var slicedSubjectRect = intersectRects(subjectRect, initialHit.rect); + if (slicedSubjectRect) { + adjustedPoint = getRectCenter(slicedSubjectRect); + } + } + this.coordAdjust = diffPoints(adjustedPoint, origPoint); + } + else { + this.coordAdjust = { left: 0, top: 0 }; + } + }; + HitDragging.prototype.handleMove = function (ev, forceHandle) { + var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top); + if (forceHandle || !isHitsEqual(this.movingHit, hit)) { + this.movingHit = hit; + this.emitter.trigger('hitupdate', hit, false, ev); + } + }; + HitDragging.prototype.prepareHits = function () { + this.offsetTrackers = mapHash(this.droppableStore, function (interactionSettings) { + interactionSettings.component.prepareHits(); + return new OffsetTracker(interactionSettings.el); + }); + }; + HitDragging.prototype.releaseHits = function () { + var offsetTrackers = this.offsetTrackers; + for (var id in offsetTrackers) { + offsetTrackers[id].destroy(); + } + this.offsetTrackers = {}; + }; + HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) { + var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers; + var bestHit = null; + for (var id in droppableStore) { + var component = droppableStore[id].component; + var offsetTracker = offsetTrackers[id]; + if (offsetTracker && // wasn't destroyed mid-drag + offsetTracker.isWithinClipping(offsetLeft, offsetTop)) { + var originLeft = offsetTracker.computeLeft(); + var originTop = offsetTracker.computeTop(); + var positionLeft = offsetLeft - originLeft; + var positionTop = offsetTop - originTop; + var origRect = offsetTracker.origRect; + var width = origRect.right - origRect.left; + var height = origRect.bottom - origRect.top; + if ( + // must be within the element's bounds + positionLeft >= 0 && positionLeft < width && + positionTop >= 0 && positionTop < height) { + var hit = component.queryHit(positionLeft, positionTop, width, height); + var dateProfile = component.context.getCurrentData().dateProfile; + if (hit && + ( + // make sure the hit is within activeRange, meaning it's not a deal cell + rangeContainsRange(dateProfile.activeRange, hit.dateSpan.range)) && + (!bestHit || hit.layer > bestHit.layer)) { + // TODO: better way to re-orient rectangle + hit.rect.left += originLeft; + hit.rect.right += originLeft; + hit.rect.top += originTop; + hit.rect.bottom += originTop; + bestHit = hit; + } + } + } + } + return bestHit; + }; + return HitDragging; + }()); + function isHitsEqual(hit0, hit1) { + if (!hit0 && !hit1) { + return true; + } + if (Boolean(hit0) !== Boolean(hit1)) { + return false; + } + return isDateSpansEqual(hit0.dateSpan, hit1.dateSpan); + } + + function buildDatePointApiWithContext(dateSpan, context) { + var props = {}; + for (var _i = 0, _a = context.pluginHooks.datePointTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(props, transform(dateSpan, context)); + } + __assign(props, buildDatePointApi(dateSpan, context.dateEnv)); + return props; + } + function buildDatePointApi(span, dateEnv) { + return { + date: dateEnv.toDate(span.range.start), + dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }), + allDay: span.allDay + }; + } + + /* + Monitors when the user clicks on a specific date/time of a component. + A pointerdown+pointerup on the same "hit" constitutes a click. + */ + var DateClicking = /** @class */ (function (_super) { + __extends(DateClicking, _super); + function DateClicking(settings) { + var _this = _super.call(this, settings) || this; + _this.handlePointerDown = function (pev) { + var dragging = _this.dragging; + var downEl = pev.origEvent.target; + // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired + dragging.setIgnoreMove(!_this.component.isValidDateDownEl(downEl)); + }; + // won't even fire if moving was ignored + _this.handleDragEnd = function (ev) { + var component = _this.component; + var pointer = _this.dragging.pointer; + if (!pointer.wasTouchScroll) { + var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit; + if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) { + var context = component.context; + var arg = __assign(__assign({}, buildDatePointApiWithContext(initialHit.dateSpan, context)), { dayEl: initialHit.dayEl, jsEvent: ev.origEvent, view: context.viewApi || context.calendarApi.view }); + context.emitter.trigger('dateClick', arg); + } + } + }; + // we DO want to watch pointer moves because otherwise finalHit won't get populated + _this.dragging = new FeaturefulElementDragging(settings.el); + _this.dragging.autoScroller.isEnabled = false; + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + DateClicking.prototype.destroy = function () { + this.dragging.destroy(); + }; + return DateClicking; + }(Interaction)); + + /* + Tracks when the user selects a portion of time of a component, + constituted by a drag over date cells, with a possible delay at the beginning of the drag. + */ + var DateSelecting = /** @class */ (function (_super) { + __extends(DateSelecting, _super); + function DateSelecting(settings) { + var _this = _super.call(this, settings) || this; + _this.dragSelection = null; + _this.handlePointerDown = function (ev) { + var _a = _this, component = _a.component, dragging = _a.dragging; + var options = component.context.options; + var canSelect = options.selectable && + component.isValidDateDownEl(ev.origEvent.target); + // don't bother to watch expensive moves if component won't do selection + dragging.setIgnoreMove(!canSelect); + // if touch, require user to hold down + dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null; + }; + _this.handleDragStart = function (ev) { + _this.component.context.calendarApi.unselect(ev); // unselect previous selections + }; + _this.handleHitUpdate = function (hit, isFinal) { + var context = _this.component.context; + var dragSelection = null; + var isInvalid = false; + if (hit) { + dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, context.pluginHooks.dateSelectionTransformers); + if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) { + isInvalid = true; + dragSelection = null; + } + } + if (dragSelection) { + context.dispatch({ type: 'SELECT_DATES', selection: dragSelection }); + } + else if (!isFinal) { // only unselect if moved away while dragging + context.dispatch({ type: 'UNSELECT_DATES' }); + } + if (!isInvalid) { + enableCursor(); + } + else { + disableCursor(); + } + if (!isFinal) { + _this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging + } + }; + _this.handlePointerUp = function (pev) { + if (_this.dragSelection) { + // selection is already rendered, so just need to report selection + triggerDateSelect(_this.dragSelection, pev, _this.component.context); + _this.dragSelection = null; + } + }; + var component = settings.component; + var options = component.context.options; + var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); + dragging.touchScrollAllowed = false; + dragging.minDistance = options.selectMinDistance || 0; + dragging.autoScroller.isEnabled = options.dragScroll; + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('pointerup', _this.handlePointerUp); + return _this; + } + DateSelecting.prototype.destroy = function () { + this.dragging.destroy(); + }; + return DateSelecting; + }(Interaction)); + function getComponentTouchDelay(component) { + var options = component.context.options; + var delay = options.selectLongPressDelay; + if (delay == null) { + delay = options.longPressDelay; + } + return delay; + } + function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) { + var dateSpan0 = hit0.dateSpan; + var dateSpan1 = hit1.dateSpan; + var ms = [ + dateSpan0.range.start, + dateSpan0.range.end, + dateSpan1.range.start, + dateSpan1.range.end + ]; + ms.sort(compareNumbers); + var props = {}; + for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) { + var transformer = dateSelectionTransformers_1[_i]; + var res = transformer(hit0, hit1); + if (res === false) { + return null; + } + else if (res) { + __assign(props, res); + } + } + props.range = { start: ms[0], end: ms[3] }; + props.allDay = dateSpan0.allDay; + return props; + } + + var EventDragging = /** @class */ (function (_super) { + __extends(EventDragging, _super); + function EventDragging(settings) { + var _this = _super.call(this, settings) || this; + // internal state + _this.subjectEl = null; + _this.subjectSeg = null; // the seg being selected/dragged + _this.isDragging = false; + _this.eventRange = null; + _this.relevantEvents = null; // the events being dragged + _this.receivingContext = null; + _this.validMutation = null; + _this.mutatedRelevantEvents = null; + _this.handlePointerDown = function (ev) { + var origTarget = ev.origEvent.target; + var _a = _this, component = _a.component, dragging = _a.dragging; + var mirror = dragging.mirror; + var options = component.context.options; + var initialContext = component.context; + _this.subjectEl = ev.subjectEl; + var subjectSeg = _this.subjectSeg = getElSeg(ev.subjectEl); + var eventRange = _this.eventRange = subjectSeg.eventRange; + var eventInstanceId = eventRange.instance.instanceId; + _this.relevantEvents = getRelevantEvents(initialContext.getCurrentData().eventStore, eventInstanceId); + dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance; + dragging.delay = + // only do a touch delay if touch and this event hasn't been selected yet + (ev.isTouch && eventInstanceId !== component.props.eventSelection) ? + getComponentTouchDelay$1(component) : + null; + mirror.parentNode = elementClosest(origTarget, '.fc'); + mirror.revertDuration = options.dragRevertDuration; + var isValid = component.isValidSegDownEl(origTarget) && + !elementClosest(origTarget, '.fc-event-resizer'); // NOT on a resizer + dragging.setIgnoreMove(!isValid); + // disable dragging for elements that are resizable (ie, selectable) + // but are not draggable + _this.isDragging = isValid && + ev.subjectEl.classList.contains('fc-event-draggable'); + }; + _this.handleDragStart = function (ev) { + var initialContext = _this.component.context; + var eventRange = _this.eventRange; + var eventInstanceId = eventRange.instance.instanceId; + if (ev.isTouch) { + // need to select a different event? + if (eventInstanceId !== _this.component.props.eventSelection) { + initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId }); + } + } + else { + // if now using mouse, but was previous touch interaction, clear selected event + initialContext.dispatch({ type: 'UNSELECT_EVENT' }); + } + if (_this.isDragging) { + initialContext.calendarApi.unselect(ev); // unselect *date* selection + initialContext.emitter.trigger('eventDragStart', { + el: _this.subjectEl, + event: new EventApi(initialContext, eventRange.def, eventRange.instance), + jsEvent: ev.origEvent, + view: initialContext.viewApi + }); + } + }; + _this.handleHitUpdate = function (hit, isFinal) { + if (!_this.isDragging) { + return; + } + var relevantEvents = _this.relevantEvents; + var initialHit = _this.hitDragging.initialHit; + var initialContext = _this.component.context; + // states based on new hit + var receivingContext = null; + var mutation = null; + var mutatedRelevantEvents = null; + var isInvalid = false; + var interaction = { + affectedEvents: relevantEvents, + mutatedEvents: createEmptyEventStore(), + isEvent: true + }; + if (hit) { + var receivingComponent = hit.component; + receivingContext = receivingComponent.context; + var receivingOptions = receivingContext.options; + if (initialContext === receivingContext || + receivingOptions.editable && receivingOptions.droppable) { + mutation = computeEventMutation(initialHit, hit, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers); + if (mutation) { + mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, receivingContext.getCurrentData().eventUiBases, mutation, receivingContext); + interaction.mutatedEvents = mutatedRelevantEvents; + if (!receivingComponent.isInteractionValid(interaction)) { + isInvalid = true; + mutation = null; + mutatedRelevantEvents = null; + interaction.mutatedEvents = createEmptyEventStore(); + } + } + } + else { + receivingContext = null; + } + } + _this.displayDrag(receivingContext, interaction); + if (!isInvalid) { + enableCursor(); + } + else { + disableCursor(); + } + if (!isFinal) { + if (initialContext === receivingContext && // TODO: write test for this + isHitsEqual(initialHit, hit)) { + mutation = null; + } + _this.dragging.setMirrorNeedsRevert(!mutation); + // render the mirror if no already-rendered mirror + // TODO: wish we could somehow wait for dispatch to guarantee render + _this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-event-mirror')); + // assign states based on new hit + _this.receivingContext = receivingContext; + _this.validMutation = mutation; + _this.mutatedRelevantEvents = mutatedRelevantEvents; + } + }; + _this.handlePointerUp = function () { + if (!_this.isDragging) { + _this.cleanup(); // because handleDragEnd won't fire + } + }; + _this.handleDragEnd = function (ev) { + if (_this.isDragging) { + var initialContext_1 = _this.component.context; + var initialView = initialContext_1.viewApi; + var _a = _this, receivingContext_1 = _a.receivingContext, validMutation = _a.validMutation; + var eventDef = _this.eventRange.def; + var eventInstance = _this.eventRange.instance; + var eventApi = new EventApi(initialContext_1, eventDef, eventInstance); + var relevantEvents_1 = _this.relevantEvents; + var mutatedRelevantEvents_1 = _this.mutatedRelevantEvents; + var finalHit = _this.hitDragging.finalHit; + _this.clearDrag(); // must happen after revert animation + initialContext_1.emitter.trigger('eventDragStop', { + el: _this.subjectEl, + event: eventApi, + jsEvent: ev.origEvent, + view: initialView + }); + if (validMutation) { + // dropped within same calendar + if (receivingContext_1 === initialContext_1) { + var updatedEventApi = new EventApi(initialContext_1, mutatedRelevantEvents_1.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents_1.instances[eventInstance.instanceId] : null); + initialContext_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: mutatedRelevantEvents_1 + }); + var eventChangeArg = { + oldEvent: eventApi, + event: updatedEventApi, + relatedEvents: buildEventApis(mutatedRelevantEvents_1, initialContext_1, eventInstance), + revert: function () { + initialContext_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents_1 // the pre-change data + }); + } + }; + var transformed = {}; + for (var _i = 0, _b = initialContext_1.getCurrentData().pluginHooks.eventDropTransformers; _i < _b.length; _i++) { + var transformer = _b[_i]; + __assign(transformed, transformer(validMutation, initialContext_1)); + } + initialContext_1.emitter.trigger('eventDrop', __assign(__assign(__assign({}, eventChangeArg), transformed), { el: ev.subjectEl, delta: validMutation.datesDelta, jsEvent: ev.origEvent, view: initialView })); + initialContext_1.emitter.trigger('eventChange', eventChangeArg); + // dropped in different calendar + } + else if (receivingContext_1) { + var eventRemoveArg = { + event: eventApi, + relatedEvents: buildEventApis(relevantEvents_1, initialContext_1, eventInstance), + revert: function () { + initialContext_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents_1 + }); + } + }; + initialContext_1.emitter.trigger('eventLeave', __assign(__assign({}, eventRemoveArg), { draggedEl: ev.subjectEl, view: initialView })); + initialContext_1.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: relevantEvents_1 + }); + initialContext_1.emitter.trigger('eventRemove', eventRemoveArg); + var addedEventDef = mutatedRelevantEvents_1.defs[eventDef.defId]; + var addedEventInstance = mutatedRelevantEvents_1.instances[eventInstance.instanceId]; + var addedEventApi = new EventApi(receivingContext_1, addedEventDef, addedEventInstance); + receivingContext_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: mutatedRelevantEvents_1 + }); + var eventAddArg = { + event: addedEventApi, + relatedEvents: buildEventApis(mutatedRelevantEvents_1, receivingContext_1, addedEventInstance), + revert: function () { + receivingContext_1.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: mutatedRelevantEvents_1 + }); + } + }; + receivingContext_1.emitter.trigger('eventAdd', eventAddArg); + if (ev.isTouch) { + receivingContext_1.dispatch({ + type: 'SELECT_EVENT', + eventInstanceId: eventInstance.instanceId + }); + } + receivingContext_1.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext_1)), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component.context.viewApi })); + receivingContext_1.emitter.trigger('eventReceive', __assign(__assign({}, eventAddArg), { draggedEl: ev.subjectEl, view: finalHit.component.context.viewApi })); + } + } + else { + initialContext_1.emitter.trigger('_noEventDrop'); + } + } + _this.cleanup(); + }; + var component = _this.component; + var options = component.context.options; + var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); + dragging.pointer.selector = EventDragging.SELECTOR; + dragging.touchScrollAllowed = false; + dragging.autoScroller.isEnabled = options.dragScroll; + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsStore); + hitDragging.useSubjectCenter = settings.useEventCenter; + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('pointerup', _this.handlePointerUp); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + EventDragging.prototype.destroy = function () { + this.dragging.destroy(); + }; + // render a drag state on the next receivingCalendar + EventDragging.prototype.displayDrag = function (nextContext, state) { + var initialContext = this.component.context; + var prevContext = this.receivingContext; + // does the previous calendar need to be cleared? + if (prevContext && prevContext !== nextContext) { + // does the initial calendar need to be cleared? + // if so, don't clear all the way. we still need to to hide the affectedEvents + if (prevContext === initialContext) { + prevContext.dispatch({ + type: 'SET_EVENT_DRAG', + state: { + affectedEvents: state.affectedEvents, + mutatedEvents: createEmptyEventStore(), + isEvent: true + } + }); + // completely clear the old calendar if it wasn't the initial + } + else { + prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + } + if (nextContext) { + nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state }); + } + }; + EventDragging.prototype.clearDrag = function () { + var initialCalendar = this.component.context; + var receivingContext = this.receivingContext; + if (receivingContext) { + receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + // the initial calendar might have an dummy drag state from displayDrag + if (initialCalendar !== receivingContext) { + initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + }; + EventDragging.prototype.cleanup = function () { + this.subjectSeg = null; + this.isDragging = false; + this.eventRange = null; + this.relevantEvents = null; + this.receivingContext = null; + this.validMutation = null; + this.mutatedRelevantEvents = null; + }; + // TODO: test this in IE11 + // QUESTION: why do we need it on the resizable??? + EventDragging.SELECTOR = '.fc-event-draggable, .fc-event-resizable'; + return EventDragging; + }(Interaction)); + function computeEventMutation(hit0, hit1, massagers) { + var dateSpan0 = hit0.dateSpan; + var dateSpan1 = hit1.dateSpan; + var date0 = dateSpan0.range.start; + var date1 = dateSpan1.range.start; + var standardProps = {}; + if (dateSpan0.allDay !== dateSpan1.allDay) { + standardProps.allDay = dateSpan1.allDay; + standardProps.hasEnd = hit1.component.context.options.allDayMaintainDuration; + if (dateSpan1.allDay) { + // means date1 is already start-of-day, + // but date0 needs to be converted + date0 = startOfDay(date0); + } + } + var delta = diffDates(date0, date1, hit0.component.context.dateEnv, hit0.component === hit1.component ? + hit0.component.largeUnit : + null); + if (delta.milliseconds) { // has hours/minutes/seconds + standardProps.allDay = false; + } + var mutation = { + datesDelta: delta, + standardProps: standardProps + }; + for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) { + var massager = massagers_1[_i]; + massager(mutation, hit0, hit1); + } + return mutation; + } + function getComponentTouchDelay$1(component) { + var options = component.context.options; + var delay = options.eventLongPressDelay; + if (delay == null) { + delay = options.longPressDelay; + } + return delay; + } + + var EventResizing = /** @class */ (function (_super) { + __extends(EventResizing, _super); + function EventResizing(settings) { + var _this = _super.call(this, settings) || this; + // internal state + _this.draggingSegEl = null; + _this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg? + _this.eventRange = null; + _this.relevantEvents = null; + _this.validMutation = null; + _this.mutatedRelevantEvents = null; + _this.handlePointerDown = function (ev) { + var component = _this.component; + var segEl = _this.querySegEl(ev); + var seg = getElSeg(segEl); + var eventRange = _this.eventRange = seg.eventRange; + _this.dragging.minDistance = component.context.options.eventDragMinDistance; + // if touch, need to be working with a selected event + _this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) || + (ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId)); + }; + _this.handleDragStart = function (ev) { + var context = _this.component.context; + var eventRange = _this.eventRange; + _this.relevantEvents = getRelevantEvents(context.getCurrentData().eventStore, _this.eventRange.instance.instanceId); + var segEl = _this.querySegEl(ev); + _this.draggingSegEl = segEl; + _this.draggingSeg = getElSeg(segEl); + context.calendarApi.unselect(); + context.emitter.trigger('eventResizeStart', { + el: segEl, + event: new EventApi(context, eventRange.def, eventRange.instance), + jsEvent: ev.origEvent, + view: context.viewApi + }); + }; + _this.handleHitUpdate = function (hit, isFinal, ev) { + var context = _this.component.context; + var relevantEvents = _this.relevantEvents; + var initialHit = _this.hitDragging.initialHit; + var eventInstance = _this.eventRange.instance; + var mutation = null; + var mutatedRelevantEvents = null; + var isInvalid = false; + var interaction = { + affectedEvents: relevantEvents, + mutatedEvents: createEmptyEventStore(), + isEvent: true + }; + if (hit) { + mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-event-resizer-start'), eventInstance.range, context.pluginHooks.eventResizeJoinTransforms); + } + if (mutation) { + mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context); + interaction.mutatedEvents = mutatedRelevantEvents; + if (!_this.component.isInteractionValid(interaction)) { + isInvalid = true; + mutation = null; + mutatedRelevantEvents = null; + interaction.mutatedEvents = null; + } + } + if (mutatedRelevantEvents) { + context.dispatch({ + type: 'SET_EVENT_RESIZE', + state: interaction + }); + } + else { + context.dispatch({ type: 'UNSET_EVENT_RESIZE' }); + } + if (!isInvalid) { + enableCursor(); + } + else { + disableCursor(); + } + if (!isFinal) { + if (mutation && isHitsEqual(initialHit, hit)) { + mutation = null; + } + _this.validMutation = mutation; + _this.mutatedRelevantEvents = mutatedRelevantEvents; + } + }; + _this.handleDragEnd = function (ev) { + var context = _this.component.context; + var eventDef = _this.eventRange.def; + var eventInstance = _this.eventRange.instance; + var eventApi = new EventApi(context, eventDef, eventInstance); + var relevantEvents = _this.relevantEvents; + var mutatedRelevantEvents = _this.mutatedRelevantEvents; + context.emitter.trigger('eventResizeStop', { + el: _this.draggingSegEl, + event: eventApi, + jsEvent: ev.origEvent, + view: context.viewApi + }); + if (_this.validMutation) { + var updatedEventApi = new EventApi(context, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null); + context.dispatch({ + type: 'MERGE_EVENTS', + eventStore: mutatedRelevantEvents + }); + var eventChangeArg = { + oldEvent: eventApi, + event: updatedEventApi, + relatedEvents: buildEventApis(mutatedRelevantEvents, context, eventInstance), + revert: function () { + context.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents // the pre-change events + }); + } + }; + context.emitter.trigger('eventResize', __assign(__assign({}, eventChangeArg), { el: _this.draggingSegEl, startDelta: _this.validMutation.startDelta || createDuration(0), endDelta: _this.validMutation.endDelta || createDuration(0), jsEvent: ev.origEvent, view: context.viewApi })); + context.emitter.trigger('eventChange', eventChangeArg); + } + else { + context.emitter.trigger('_noEventResize'); + } + // reset all internal state + _this.draggingSeg = null; + _this.relevantEvents = null; + _this.validMutation = null; + // okay to keep eventInstance around. useful to set it in handlePointerDown + }; + var component = settings.component; + var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); + dragging.pointer.selector = '.fc-event-resizer'; + dragging.touchScrollAllowed = false; + dragging.autoScroller.isEnabled = component.context.options.dragScroll; + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + EventResizing.prototype.destroy = function () { + this.dragging.destroy(); + }; + EventResizing.prototype.querySegEl = function (ev) { + return elementClosest(ev.subjectEl, '.fc-event'); + }; + return EventResizing; + }(Interaction)); + function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) { + var dateEnv = hit0.component.context.dateEnv; + var date0 = hit0.dateSpan.range.start; + var date1 = hit1.dateSpan.range.start; + var delta = diffDates(date0, date1, dateEnv, hit0.component.largeUnit); + var props = {}; + for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) { + var transform = transforms_1[_i]; + var res = transform(hit0, hit1); + if (res === false) { + return null; + } + else if (res) { + __assign(props, res); + } + } + if (isFromStart) { + if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) { + props.startDelta = delta; + return props; + } + } + else { + if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) { + props.endDelta = delta; + return props; + } + } + return null; + } + + var UnselectAuto = /** @class */ (function () { + function UnselectAuto(context) { + var _this = this; + this.context = context; + this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system + this.matchesCancel = false; + this.matchesEvent = false; + this.onSelect = function (selectInfo) { + if (selectInfo.jsEvent) { + _this.isRecentPointerDateSelect = true; + } + }; + this.onDocumentPointerDown = function (pev) { + var unselectCancel = _this.context.options.unselectCancel; + var downEl = pev.origEvent.target; + _this.matchesCancel = !!elementClosest(downEl, unselectCancel); + _this.matchesEvent = !!elementClosest(downEl, EventDragging.SELECTOR); // interaction started on an event? + }; + this.onDocumentPointerUp = function (pev) { + var context = _this.context; + var documentPointer = _this.documentPointer; + var calendarState = context.getCurrentData(); + // touch-scrolling should never unfocus any type of selection + if (!documentPointer.wasTouchScroll) { + if (calendarState.dateSelection && // an existing date selection? + !_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp? + ) { + var unselectAuto = context.options.unselectAuto; + if (unselectAuto && (!unselectAuto || !_this.matchesCancel)) { + context.calendarApi.unselect(pev); + } + } + if (calendarState.eventSelection && // an existing event selected? + !_this.matchesEvent // interaction DIDN'T start on an event + ) { + context.dispatch({ type: 'UNSELECT_EVENT' }); + } + } + _this.isRecentPointerDateSelect = false; + }; + var documentPointer = this.documentPointer = new PointerDragging(document); + documentPointer.shouldIgnoreMove = true; + documentPointer.shouldWatchScroll = false; + documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown); + documentPointer.emitter.on('pointerup', this.onDocumentPointerUp); + /* + TODO: better way to know about whether there was a selection with the pointer + */ + context.emitter.on('select', this.onSelect); + } + UnselectAuto.prototype.destroy = function () { + this.context.emitter.off('select', this.onSelect); + this.documentPointer.destroy(); + }; + return UnselectAuto; + }()); + + var LISTENER_REFINERS = { + dateClick: identity, + eventDragStart: identity, + eventDragStop: identity, + eventDrop: identity, + eventResizeStart: identity, + eventResizeStop: identity, + eventResize: identity, + drop: identity, + eventReceive: identity, + eventLeave: identity + }; + + /* + Given an already instantiated draggable object for one-or-more elements, + Interprets any dragging as an attempt to drag an events that lives outside + of a calendar onto a calendar. + */ + var ExternalElementDragging = /** @class */ (function () { + function ExternalElementDragging(dragging, suppliedDragMeta) { + var _this = this; + this.receivingContext = null; + this.droppableEvent = null; // will exist for all drags, even if create:false + this.suppliedDragMeta = null; + this.dragMeta = null; + this.handleDragStart = function (ev) { + _this.dragMeta = _this.buildDragMeta(ev.subjectEl); + }; + this.handleHitUpdate = function (hit, isFinal, ev) { + var dragging = _this.hitDragging.dragging; + var receivingContext = null; + var droppableEvent = null; + var isInvalid = false; + var interaction = { + affectedEvents: createEmptyEventStore(), + mutatedEvents: createEmptyEventStore(), + isEvent: _this.dragMeta.create + }; + if (hit) { + receivingContext = hit.component.context; + if (_this.canDropElOnCalendar(ev.subjectEl, receivingContext)) { + droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingContext); + interaction.mutatedEvents = eventTupleToStore(droppableEvent); + isInvalid = !isInteractionValid(interaction, receivingContext); + if (isInvalid) { + interaction.mutatedEvents = createEmptyEventStore(); + droppableEvent = null; + } + } + } + _this.displayDrag(receivingContext, interaction); + // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?) + // TODO: wish we could somehow wait for dispatch to guarantee render + dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-event-mirror')); + if (!isInvalid) { + enableCursor(); + } + else { + disableCursor(); + } + if (!isFinal) { + dragging.setMirrorNeedsRevert(!droppableEvent); + _this.receivingContext = receivingContext; + _this.droppableEvent = droppableEvent; + } + }; + this.handleDragEnd = function (pev) { + var _a = _this, receivingContext = _a.receivingContext, droppableEvent = _a.droppableEvent; + _this.clearDrag(); + if (receivingContext && droppableEvent) { + var finalHit = _this.hitDragging.finalHit; + var finalView = finalHit.component.context.viewApi; + var dragMeta = _this.dragMeta; + receivingContext.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView })); + if (dragMeta.create) { + var addingEvents_1 = eventTupleToStore(droppableEvent); + receivingContext.dispatch({ + type: 'MERGE_EVENTS', + eventStore: addingEvents_1 + }); + if (pev.isTouch) { + receivingContext.dispatch({ + type: 'SELECT_EVENT', + eventInstanceId: droppableEvent.instance.instanceId + }); + } + // signal that an external event landed + receivingContext.emitter.trigger('eventReceive', { + event: new EventApi(receivingContext, droppableEvent.def, droppableEvent.instance), + relatedEvents: [], + revert: function () { + receivingContext.dispatch({ + type: 'REMOVE_EVENTS', + eventStore: addingEvents_1 + }); + }, + draggedEl: pev.subjectEl, + view: finalView + }); + } + } + _this.receivingContext = null; + _this.droppableEvent = null; + }; + var hitDragging = this.hitDragging = new HitDragging(dragging, interactionSettingsStore); + hitDragging.requireInitial = false; // will start outside of a component + hitDragging.emitter.on('dragstart', this.handleDragStart); + hitDragging.emitter.on('hitupdate', this.handleHitUpdate); + hitDragging.emitter.on('dragend', this.handleDragEnd); + this.suppliedDragMeta = suppliedDragMeta; + } + ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) { + if (typeof this.suppliedDragMeta === 'object') { + return parseDragMeta(this.suppliedDragMeta); + } + else if (typeof this.suppliedDragMeta === 'function') { + return parseDragMeta(this.suppliedDragMeta(subjectEl)); + } + else { + return getDragMetaFromEl(subjectEl); + } + }; + ExternalElementDragging.prototype.displayDrag = function (nextContext, state) { + var prevContext = this.receivingContext; + if (prevContext && prevContext !== nextContext) { + prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + if (nextContext) { + nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state }); + } + }; + ExternalElementDragging.prototype.clearDrag = function () { + if (this.receivingContext) { + this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + }; + ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingContext) { + var dropAccept = receivingContext.options.dropAccept; + if (typeof dropAccept === 'function') { + return dropAccept.call(receivingContext.calendarApi, el); + } + else if (typeof dropAccept === 'string' && dropAccept) { + return Boolean(elementMatches(el, dropAccept)); + } + return true; + }; + return ExternalElementDragging; + }()); + // Utils for computing event store from the DragMeta + // ---------------------------------------------------------------------------------------------------- + function computeEventForDateSpan(dateSpan, dragMeta, context) { + var defProps = __assign({}, dragMeta.leftoverProps); + for (var _i = 0, _a = context.pluginHooks.externalDefTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(defProps, transform(dateSpan, dragMeta)); + } + var _b = refineEventDef(defProps, context), refined = _b.refined, extra = _b.extra; + var def = parseEventDef(refined, extra, dragMeta.sourceId, dateSpan.allDay, context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd + context); + var start = dateSpan.range.start; + // only rely on time info if drop zone is all-day, + // otherwise, we already know the time + if (dateSpan.allDay && dragMeta.startTime) { + start = context.dateEnv.add(start, dragMeta.startTime); + } + var end = dragMeta.duration ? + context.dateEnv.add(start, dragMeta.duration) : + getDefaultEventEnd(dateSpan.allDay, start, context); + var instance = createEventInstance(def.defId, { start: start, end: end }); + return { def: def, instance: instance }; + } + // Utils for extracting data from element + // ---------------------------------------------------------------------------------------------------- + function getDragMetaFromEl(el) { + var str = getEmbeddedElData(el, 'event'); + var obj = str ? + JSON.parse(str) : + { create: false }; // if no embedded data, assume no event creation + return parseDragMeta(obj); + } + config.dataAttrPrefix = ''; + function getEmbeddedElData(el, name) { + var prefix = config.dataAttrPrefix; + var prefixedName = (prefix ? prefix + '-' : '') + name; + return el.getAttribute('data-' + prefixedName) || ''; + } + + /* + Makes an element (that is *external* to any calendar) draggable. + Can pass in data that determines how an event will be created when dropped onto a calendar. + Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system. + */ + var ExternalDraggable = /** @class */ (function () { + function ExternalDraggable(el, settings) { + var _this = this; + if (settings === void 0) { settings = {}; } + this.handlePointerDown = function (ev) { + var dragging = _this.dragging; + var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay; + dragging.minDistance = + minDistance != null ? + minDistance : + (ev.isTouch ? 0 : BASE_OPTION_DEFAULTS.eventDragMinDistance); + dragging.delay = + ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv + (longPressDelay != null ? longPressDelay : BASE_OPTION_DEFAULTS.longPressDelay) : + 0; + }; + this.handleDragStart = function (ev) { + if (ev.isTouch && + _this.dragging.delay && + ev.subjectEl.classList.contains('fc-event')) { + _this.dragging.mirror.getMirrorEl().classList.add('fc-event-selected'); + } + }; + this.settings = settings; + var dragging = this.dragging = new FeaturefulElementDragging(el); + dragging.touchScrollAllowed = false; + if (settings.itemSelector != null) { + dragging.pointer.selector = settings.itemSelector; + } + if (settings.appendTo != null) { + dragging.mirror.parentNode = settings.appendTo; // TODO: write tests + } + dragging.emitter.on('pointerdown', this.handlePointerDown); + dragging.emitter.on('dragstart', this.handleDragStart); + new ExternalElementDragging(dragging, settings.eventData); + } + ExternalDraggable.prototype.destroy = function () { + this.dragging.destroy(); + }; + return ExternalDraggable; + }()); + + /* + Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements. + The third-party system is responsible for drawing the visuals effects of the drag. + This class simply monitors for pointer movements and fires events. + It also has the ability to hide the moving element (the "mirror") during the drag. + */ + var InferredElementDragging = /** @class */ (function (_super) { + __extends(InferredElementDragging, _super); + function InferredElementDragging(containerEl) { + var _this = _super.call(this, containerEl) || this; + _this.shouldIgnoreMove = false; + _this.mirrorSelector = ''; + _this.currentMirrorEl = null; + _this.handlePointerDown = function (ev) { + _this.emitter.trigger('pointerdown', ev); + if (!_this.shouldIgnoreMove) { + // fire dragstart right away. does not support delay or min-distance + _this.emitter.trigger('dragstart', ev); + } + }; + _this.handlePointerMove = function (ev) { + if (!_this.shouldIgnoreMove) { + _this.emitter.trigger('dragmove', ev); + } + }; + _this.handlePointerUp = function (ev) { + _this.emitter.trigger('pointerup', ev); + if (!_this.shouldIgnoreMove) { + // fire dragend right away. does not support a revert animation + _this.emitter.trigger('dragend', ev); + } + }; + var pointer = _this.pointer = new PointerDragging(containerEl); + pointer.emitter.on('pointerdown', _this.handlePointerDown); + pointer.emitter.on('pointermove', _this.handlePointerMove); + pointer.emitter.on('pointerup', _this.handlePointerUp); + return _this; + } + InferredElementDragging.prototype.destroy = function () { + this.pointer.destroy(); + }; + InferredElementDragging.prototype.setIgnoreMove = function (bool) { + this.shouldIgnoreMove = bool; + }; + InferredElementDragging.prototype.setMirrorIsVisible = function (bool) { + if (bool) { + // restore a previously hidden element. + // use the reference in case the selector class has already been removed. + if (this.currentMirrorEl) { + this.currentMirrorEl.style.visibility = ''; + this.currentMirrorEl = null; + } + } + else { + var mirrorEl = this.mirrorSelector ? + document.querySelector(this.mirrorSelector) : + null; + if (mirrorEl) { + this.currentMirrorEl = mirrorEl; + mirrorEl.style.visibility = 'hidden'; + } + } + }; + return InferredElementDragging; + }(ElementDragging)); + + /* + Bridges third-party drag-n-drop systems with FullCalendar. + Must be instantiated and destroyed by caller. + */ + var ThirdPartyDraggable = /** @class */ (function () { + function ThirdPartyDraggable(containerOrSettings, settings) { + var containerEl = document; + if ( + // wish we could just test instanceof EventTarget, but doesn't work in IE11 + containerOrSettings === document || + containerOrSettings instanceof Element) { + containerEl = containerOrSettings; + settings = settings || {}; + } + else { + settings = (containerOrSettings || {}); + } + var dragging = this.dragging = new InferredElementDragging(containerEl); + if (typeof settings.itemSelector === 'string') { + dragging.pointer.selector = settings.itemSelector; + } + else if (containerEl === document) { + dragging.pointer.selector = '[data-event]'; + } + if (typeof settings.mirrorSelector === 'string') { + dragging.mirrorSelector = settings.mirrorSelector; + } + new ExternalElementDragging(dragging, settings.eventData); + } + ThirdPartyDraggable.prototype.destroy = function () { + this.dragging.destroy(); + }; + return ThirdPartyDraggable; + }()); + + var interactionPlugin = createPlugin({ + componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing], + calendarInteractions: [UnselectAuto], + elementDraggingImpl: FeaturefulElementDragging, + listenerRefiners: LISTENER_REFINERS + }); + + /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells. + ----------------------------------------------------------------------------------------------------------------------*/ + // It is a manager for a Table subcomponent, which does most of the heavy lifting. + // It is responsible for managing width/height. + var TableView = /** @class */ (function (_super) { + __extends(TableView, _super); + function TableView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.headerElRef = createRef(); + return _this; + } + TableView.prototype.renderSimpleLayout = function (headerRowContent, bodyContent) { + var _a = this, props = _a.props, context = _a.context; + var sections = []; + var stickyHeaderDates = getStickyHeaderDates(context.options); + if (headerRowContent) { + sections.push({ + type: 'header', + key: 'header', + isSticky: stickyHeaderDates, + chunk: { + elRef: this.headerElRef, + tableClassName: 'fc-col-header', + rowContent: headerRowContent + } + }); + } + sections.push({ + type: 'body', + key: 'body', + liquid: true, + chunk: { content: bodyContent } + }); + return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') }, + createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, cols: [] /* TODO: make optional? */, sections: sections }))); })); + }; + TableView.prototype.renderHScrollLayout = function (headerRowContent, bodyContent, colCnt, dayMinWidth) { + var ScrollGrid = this.context.pluginHooks.scrollGridImpl; + if (!ScrollGrid) { + throw new Error('No ScrollGrid implementation'); + } + var _a = this, props = _a.props, context = _a.context; + var stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options); + var stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options); + var sections = []; + if (headerRowContent) { + sections.push({ + type: 'header', + key: 'header', + isSticky: stickyHeaderDates, + chunks: [{ + key: 'main', + elRef: this.headerElRef, + tableClassName: 'fc-col-header', + rowContent: headerRowContent + }] + }); + } + sections.push({ + type: 'body', + key: 'body', + liquid: true, + chunks: [{ + key: 'main', + content: bodyContent + }] + }); + if (stickyFooterScrollbar) { + sections.push({ + type: 'footer', + key: 'footer', + isSticky: true, + chunks: [{ + key: 'main', + content: renderScrollShim + }] + }); + } + return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') }, + createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, colGroups: [{ cols: [{ span: colCnt, minWidth: dayMinWidth }] }], sections: sections }))); })); + }; + return TableView; + }(DateComponent)); + + function splitSegsByRow(segs, rowCnt) { + var byRow = []; + for (var i = 0; i < rowCnt; i++) { + byRow[i] = []; + } + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + byRow[seg.row].push(seg); + } + return byRow; + } + function splitSegsByFirstCol(segs, colCnt) { + var byCol = []; + for (var i = 0; i < colCnt; i++) { + byCol[i] = []; + } + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + byCol[seg.firstCol].push(seg); + } + return byCol; + } + function splitInteractionByRow(ui, rowCnt) { + var byRow = []; + if (!ui) { + for (var i = 0; i < rowCnt; i++) { + byRow[i] = null; + } + } + else { + for (var i = 0; i < rowCnt; i++) { + byRow[i] = { + affectedInstances: ui.affectedInstances, + isEvent: ui.isEvent, + segs: [] + }; + } + for (var _i = 0, _a = ui.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + byRow[seg.row].segs.push(seg); + } + } + return byRow; + } + + var DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' }); + var TableCell = /** @class */ (function (_super) { + __extends(TableCell, _super); + function TableCell() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.handleRootEl = function (el) { + _this.rootEl = el; + setRef(_this.props.elRef, el); + }; + _this.handleMoreLinkClick = function (ev) { + var props = _this.props; + if (props.onMoreClick) { + var allSegs = props.segsByEachCol; + var hiddenSegs = allSegs.filter(function (seg) { return props.segIsHidden[seg.eventRange.instance.instanceId]; }); + props.onMoreClick({ + date: props.date, + allSegs: allSegs, + hiddenSegs: hiddenSegs, + moreCnt: props.moreCnt, + dayEl: _this.rootEl, + ev: ev + }); + } + }; + return _this; + } + TableCell.prototype.render = function () { + var _this = this; + var _a = this.context, options = _a.options, viewApi = _a.viewApi; + var props = this.props; + var date = props.date, dateProfile = props.dateProfile; + var hookProps = { + num: props.moreCnt, + text: props.buildMoreLinkText(props.moreCnt), + view: viewApi + }; + var navLinkAttrs = options.navLinks + ? { 'data-navlink': buildNavLinkData(date, 'week'), tabIndex: 0 } + : {}; + return (createElement(DayCellRoot, { date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, elRef: this.handleRootEl }, function (rootElRef, classNames, rootDataAttrs, isDisabled) { return (createElement("td", __assign({ ref: rootElRef, className: ['fc-daygrid-day'].concat(classNames, props.extraClassNames || []).join(' ') }, rootDataAttrs, props.extraDataAttrs), + createElement("div", { className: 'fc-daygrid-day-frame fc-scrollgrid-sync-inner', ref: props.innerElRef /* different from hook system! RENAME */ }, + props.showWeekNumber && + createElement(WeekNumberRoot, { date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("a", __assign({ ref: rootElRef, className: ['fc-daygrid-week-number'].concat(classNames).join(' ') }, navLinkAttrs), innerContent)); }), + !isDisabled && + createElement(TableCellTop, { date: date, dateProfile: dateProfile, showDayNumber: props.showDayNumber, forceDayTop: props.forceDayTop, todayRange: props.todayRange, extraHookProps: props.extraHookProps }), + createElement("div", { className: 'fc-daygrid-day-events', ref: props.fgContentElRef, style: { paddingBottom: props.fgPaddingBottom } }, + props.fgContent, + Boolean(props.moreCnt) && + createElement("div", { className: 'fc-daygrid-day-bottom', style: { marginTop: props.moreMarginTop } }, + createElement(RenderHook, { hookProps: hookProps, classNames: options.moreLinkClassNames, content: options.moreLinkContent, defaultContent: renderMoreLinkInner, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("a", { onClick: _this.handleMoreLinkClick, ref: rootElRef, className: ['fc-daygrid-more-link'].concat(classNames).join(' ') }, innerContent)); }))), + createElement("div", { className: 'fc-daygrid-day-bg' }, props.bgContent)))); })); + }; + return TableCell; + }(DateComponent)); + function renderTopInner(props) { + return props.dayNumberText; + } + function renderMoreLinkInner(props) { + return props.text; + } + var TableCellTop = /** @class */ (function (_super) { + __extends(TableCellTop, _super); + function TableCellTop() { + return _super !== null && _super.apply(this, arguments) || this; + } + TableCellTop.prototype.render = function () { + var props = this.props; + var navLinkAttrs = this.context.options.navLinks + ? { 'data-navlink': buildNavLinkData(props.date), tabIndex: 0 } + : {}; + return (createElement(DayCellContent, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, defaultContent: renderTopInner }, function (innerElRef, innerContent) { return ((innerContent || props.forceDayTop) && + createElement("div", { className: 'fc-daygrid-day-top', ref: innerElRef }, + createElement("a", __assign({ className: 'fc-daygrid-day-number' }, navLinkAttrs), innerContent || createElement(Fragment, null, "\u00A0")))); })); + }; + return TableCellTop; + }(BaseComponent)); + + var DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({ + hour: 'numeric', + minute: '2-digit', + omitZeroMinute: true, + meridiem: 'narrow' + }); + function hasListItemDisplay(seg) { + var display = seg.eventRange.ui.display; + return display === 'list-item' || (display === 'auto' && + !seg.eventRange.def.allDay && + seg.firstCol === seg.lastCol && // can't be multi-day + seg.isStart && // " + seg.isEnd // " + ); + } + + var TableListItemEvent = /** @class */ (function (_super) { + __extends(TableListItemEvent, _super); + function TableListItemEvent() { + return _super !== null && _super.apply(this, arguments) || this; + } + TableListItemEvent.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var timeFormat = context.options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT; + var timeText = buildSegTimeText(props.seg, timeFormat, context, true, props.defaultDisplayEventEnd); + return (createElement(EventRoot, { seg: props.seg, timeText: timeText, defaultContent: renderInnerContent$2, isDragging: props.isDragging, isResizing: false, isDateSelecting: false, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent) { return ( // we don't use styles! + createElement("a", __assign({ className: ['fc-daygrid-event', 'fc-daygrid-dot-event'].concat(classNames).join(' '), ref: rootElRef }, getSegAnchorAttrs$1(props.seg)), innerContent)); })); + }; + return TableListItemEvent; + }(BaseComponent)); + function renderInnerContent$2(innerProps) { + return (createElement(Fragment, null, + createElement("div", { className: 'fc-daygrid-event-dot', style: { borderColor: innerProps.borderColor || innerProps.backgroundColor } }), + innerProps.timeText && + createElement("div", { className: 'fc-event-time' }, innerProps.timeText), + createElement("div", { className: 'fc-event-title' }, innerProps.event.title || createElement(Fragment, null, "\u00A0")))); + } + function getSegAnchorAttrs$1(seg) { + var url = seg.eventRange.def.url; + return url ? { href: url } : {}; + } + + var TableBlockEvent = /** @class */ (function (_super) { + __extends(TableBlockEvent, _super); + function TableBlockEvent() { + return _super !== null && _super.apply(this, arguments) || this; + } + TableBlockEvent.prototype.render = function () { + var props = this.props; + return (createElement(StandardEvent, __assign({}, props, { extraClassNames: ['fc-daygrid-event', 'fc-daygrid-block-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.seg.eventRange.def.allDay }))); + }; + return TableBlockEvent; + }(BaseComponent)); + + function computeFgSegPlacement(// for one row. TODO: print mode? + cellModels, segs, dayMaxEvents, dayMaxEventRows, eventHeights, maxContentHeight, colCnt, eventOrderSpecs) { + var colPlacements = []; // if event spans multiple cols, its present in each col + var moreCnts = []; // by-col + var segIsHidden = {}; + var segTops = {}; // always populated for each seg + var segMarginTops = {}; // simetimes populated for each seg + var moreTops = {}; + var paddingBottoms = {}; // for each cell's inner-wrapper div + for (var i = 0; i < colCnt; i++) { + colPlacements.push([]); + moreCnts.push(0); + } + segs = sortEventSegs(segs, eventOrderSpecs); + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + var instanceId = seg.eventRange.instance.instanceId; + var eventHeight = eventHeights[instanceId + ':' + seg.firstCol]; + placeSeg(seg, eventHeight || 0); // will keep colPlacements sorted by top + } + if (dayMaxEvents === true || dayMaxEventRows === true) { + limitByMaxHeight(moreCnts, segIsHidden, colPlacements, maxContentHeight); // populates moreCnts/segIsHidden + } + else if (typeof dayMaxEvents === 'number') { + limitByMaxEvents(moreCnts, segIsHidden, colPlacements, dayMaxEvents); // populates moreCnts/segIsHidden + } + else if (typeof dayMaxEventRows === 'number') { + limitByMaxRows(moreCnts, segIsHidden, colPlacements, dayMaxEventRows); // populates moreCnts/segIsHidden + } + // computes segTops/segMarginTops/moreTops/paddingBottoms + for (var col = 0; col < colCnt; col++) { + var placements = colPlacements[col]; + var currentNonAbsBottom = 0; + var currentAbsHeight = 0; + for (var _a = 0, placements_1 = placements; _a < placements_1.length; _a++) { + var placement = placements_1[_a]; + var seg = placement.seg; + if (!segIsHidden[seg.eventRange.instance.instanceId]) { + segTops[seg.eventRange.instance.instanceId] = placement.top; // from top of container + if (seg.firstCol === seg.lastCol && seg.isStart && seg.isEnd) { // TODO: simpler way? NOT DRY + segMarginTops[seg.eventRange.instance.instanceId] = + placement.top - currentNonAbsBottom; // from previous seg bottom + currentAbsHeight = 0; + currentNonAbsBottom = placement.bottom; + } + else { // multi-col event, abs positioned + currentAbsHeight = placement.bottom - currentNonAbsBottom; + } + } + } + if (currentAbsHeight) { + if (moreCnts[col]) { + moreTops[col] = currentAbsHeight; + } + else { + paddingBottoms[col] = currentAbsHeight; + } + } + } + function placeSeg(seg, segHeight) { + if (!tryPlaceSegAt(seg, segHeight, 0)) { + for (var col = seg.firstCol; col <= seg.lastCol; col++) { + for (var _i = 0, _a = colPlacements[col]; _i < _a.length; _i++) { // will repeat multi-day segs!!!!!!! bad!!!!!! + var placement = _a[_i]; + if (tryPlaceSegAt(seg, segHeight, placement.bottom)) { + return; + } + } + } + } + } + function tryPlaceSegAt(seg, segHeight, top) { + if (canPlaceSegAt(seg, segHeight, top)) { + for (var col = seg.firstCol; col <= seg.lastCol; col++) { + var placements = colPlacements[col]; + var insertionIndex = 0; + while (insertionIndex < placements.length && + top >= placements[insertionIndex].top) { + insertionIndex++; + } + placements.splice(insertionIndex, 0, { + seg: seg, + top: top, + bottom: top + segHeight + }); + } + return true; + } + else { + return false; + } + } + function canPlaceSegAt(seg, segHeight, top) { + for (var col = seg.firstCol; col <= seg.lastCol; col++) { + for (var _i = 0, _a = colPlacements[col]; _i < _a.length; _i++) { + var placement = _a[_i]; + if (top < placement.bottom && top + segHeight > placement.top) { // collide? + return false; + } + } + } + return true; + } + // what does this do!? + for (var instanceIdAndFirstCol in eventHeights) { + if (!eventHeights[instanceIdAndFirstCol]) { + segIsHidden[instanceIdAndFirstCol.split(':')[0]] = true; + } + } + var segsByFirstCol = colPlacements.map(extractFirstColSegs); // operates on the sorted cols + var segsByEachCol = colPlacements.map(function (placements, col) { + var segs = extractAllColSegs(placements); + segs = resliceDaySegs(segs, cellModels[col].date, col); + return segs; + }); + return { + segsByFirstCol: segsByFirstCol, + segsByEachCol: segsByEachCol, + segIsHidden: segIsHidden, + segTops: segTops, + segMarginTops: segMarginTops, + moreCnts: moreCnts, + moreTops: moreTops, + paddingBottoms: paddingBottoms + }; + } + function extractFirstColSegs(oneColPlacements, col) { + var segs = []; + for (var _i = 0, oneColPlacements_1 = oneColPlacements; _i < oneColPlacements_1.length; _i++) { + var placement = oneColPlacements_1[_i]; + if (placement.seg.firstCol === col) { + segs.push(placement.seg); + } + } + return segs; + } + function extractAllColSegs(oneColPlacements) { + var segs = []; + for (var _i = 0, oneColPlacements_2 = oneColPlacements; _i < oneColPlacements_2.length; _i++) { + var placement = oneColPlacements_2[_i]; + segs.push(placement.seg); + } + return segs; + } + function limitByMaxHeight(hiddenCnts, segIsHidden, colPlacements, maxContentHeight) { + limitEvents(hiddenCnts, segIsHidden, colPlacements, true, function (placement) { + return placement.bottom <= maxContentHeight; + }); + } + function limitByMaxEvents(hiddenCnts, segIsHidden, colPlacements, dayMaxEvents) { + limitEvents(hiddenCnts, segIsHidden, colPlacements, false, function (placement, levelIndex) { + return levelIndex < dayMaxEvents; + }); + } + function limitByMaxRows(hiddenCnts, segIsHidden, colPlacements, dayMaxEventRows) { + limitEvents(hiddenCnts, segIsHidden, colPlacements, true, function (placement, levelIndex) { + return levelIndex < dayMaxEventRows; + }); + } + /* + populates the given hiddenCnts/segIsHidden, which are supplied empty. + TODO: return them instead + */ + function limitEvents(hiddenCnts, segIsHidden, colPlacements, _moreLinkConsumesLevel, isPlacementInBounds) { + var colCnt = hiddenCnts.length; + var segIsVisible = {}; // TODO: instead, use segIsHidden with true/false? + var visibleColPlacements = []; // will mirror colPlacements + for (var col = 0; col < colCnt; col++) { + visibleColPlacements.push([]); + } + for (var col = 0; col < colCnt; col++) { + var placements = colPlacements[col]; + var level = 0; + for (var _i = 0, placements_2 = placements; _i < placements_2.length; _i++) { + var placement = placements_2[_i]; + if (isPlacementInBounds(placement, level)) { + recordVisible(placement); + } + else { + recordHidden(placement, level, _moreLinkConsumesLevel); + } + // only considered a level if the seg had height + if (placement.top !== placement.bottom) { + level++; + } + } + } + function recordVisible(placement) { + var seg = placement.seg; + var instanceId = seg.eventRange.instance.instanceId; + if (!segIsVisible[instanceId]) { + segIsVisible[instanceId] = true; + for (var col = seg.firstCol; col <= seg.lastCol; col++) { + visibleColPlacements[col].push(placement); + } + } + } + function recordHidden(placement, currentLevel, moreLinkConsumesLevel) { + var seg = placement.seg; + var instanceId = seg.eventRange.instance.instanceId; + if (!segIsHidden[instanceId]) { + segIsHidden[instanceId] = true; + for (var col = seg.firstCol; col <= seg.lastCol; col++) { + var hiddenCnt = ++hiddenCnts[col]; + if (moreLinkConsumesLevel && hiddenCnt === 1) { + var doomedLevel = currentLevel - 1; + while (visibleColPlacements[col].length > doomedLevel) { + recordHidden(visibleColPlacements[col].pop(), // removes + visibleColPlacements[col].length, // will execute after the pop. will be the index of the removed placement + false); + } + } + } + } + } + } + // Given the events within an array of segment objects, reslice them to be in a single day + function resliceDaySegs(segs, dayDate, colIndex) { + var dayStart = dayDate; + var dayEnd = addDays(dayStart, 1); + var dayRange = { start: dayStart, end: dayEnd }; + var newSegs = []; + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + var eventRange = seg.eventRange; + var origRange = eventRange.range; + var slicedRange = intersectRanges(origRange, dayRange); + if (slicedRange) { + newSegs.push(__assign(__assign({}, seg), { firstCol: colIndex, lastCol: colIndex, eventRange: { + def: eventRange.def, + ui: __assign(__assign({}, eventRange.ui), { durationEditable: false }), + instance: eventRange.instance, + range: slicedRange + }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() })); + } + } + return newSegs; + } + + var TableRow = /** @class */ (function (_super) { + __extends(TableRow, _super); + function TableRow() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.cellElRefs = new RefMap(); // the for the axis */, + createElement(TimeColsSlatsBody, { slatElRefs: this.slatElRefs, axis: props.axis, slatMetas: props.slatMetas })))); + }; + TimeColsSlats.prototype.componentDidMount = function () { + this.updateSizing(); + }; + TimeColsSlats.prototype.componentDidUpdate = function () { + this.updateSizing(); + }; + TimeColsSlats.prototype.componentWillUnmount = function () { + if (this.props.onCoords) { + this.props.onCoords(null); + } + }; + TimeColsSlats.prototype.updateSizing = function () { + var props = this.props; + if (props.onCoords && + props.clientWidth !== null // means sizing has stabilized + ) { + var rootEl = this.rootElRef.current; + if (rootEl.offsetHeight) { // not hidden by css + props.onCoords(new TimeColsSlatsCoords(new PositionCache(this.rootElRef.current, collectSlatEls(this.slatElRefs.currentMap, props.slatMetas), false, true // vertical + ), this.props.dateProfile, props.slatMetas)); + } + } + }; + return TimeColsSlats; + }(BaseComponent)); + function collectSlatEls(elMap, slatMetas) { + return slatMetas.map(function (slatMeta) { return elMap[slatMeta.key]; }); + } + var TimeColsSlatsBody = /** @class */ (function (_super) { + __extends(TimeColsSlatsBody, _super); + function TimeColsSlatsBody() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeColsSlatsBody.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var options = context.options; + var slatElRefs = props.slatElRefs; + return (createElement("tbody", null, props.slatMetas.map(function (slatMeta, i) { + var hookProps = { + time: slatMeta.time, + date: context.dateEnv.toDate(slatMeta.date), + view: context.viewApi + }; + var classNames = [ + 'fc-timegrid-slot', + 'fc-timegrid-slot-lane', + slatMeta.isLabeled ? '' : 'fc-timegrid-slot-minor' + ]; + return (createElement("tr", { key: slatMeta.key, ref: slatElRefs.createRef(slatMeta.key) }, + props.axis && + createElement(TimeColsAxisCell, __assign({}, slatMeta)), + createElement(RenderHook, { hookProps: hookProps, classNames: options.slotLaneClassNames, content: options.slotLaneContent, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-time": slatMeta.isoTimeStr }, innerContent)); }))); + }))); + }; + return TimeColsSlatsBody; + }(BaseComponent)); + var DEFAULT_SLAT_LABEL_FORMAT = createFormatter({ + hour: 'numeric', + minute: '2-digit', + omitZeroMinute: true, + meridiem: 'short' + }); + function TimeColsAxisCell(props) { + var classNames = [ + 'fc-timegrid-slot', + 'fc-timegrid-slot-label', + props.isLabeled ? 'fc-scrollgrid-shrink' : 'fc-timegrid-slot-minor' + ]; + return (createElement(ViewContextType.Consumer, null, function (context) { + if (!props.isLabeled) { + return (createElement("td", { className: classNames.join(' '), "data-time": props.isoTimeStr })); + } + else { + var dateEnv = context.dateEnv, options = context.options, viewApi = context.viewApi; + var labelFormat = // TODO: fully pre-parse + options.slotLabelFormat == null ? DEFAULT_SLAT_LABEL_FORMAT : + Array.isArray(options.slotLabelFormat) ? createFormatter(options.slotLabelFormat[0]) : + createFormatter(options.slotLabelFormat); + var hookProps = { + level: 0, + time: props.time, + date: dateEnv.toDate(props.date), + view: viewApi, + text: dateEnv.format(props.date, labelFormat) + }; + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.slotLabelClassNames, content: options.slotLabelContent, defaultContent: renderInnerContent$3, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-time": props.isoTimeStr }, + createElement("div", { className: 'fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame' }, + createElement("div", { className: 'fc-timegrid-slot-label-cushion fc-scrollgrid-shrink-cushion', ref: innerElRef }, innerContent)))); })); + } + })); + } + function renderInnerContent$3(props) { + return props.text; + } + function buildSlatMetas(slotMinTime, slotMaxTime, explicitLabelInterval, slotDuration, dateEnv) { + var dayStart = new Date(0); + var slatTime = slotMinTime; + var slatIterator = createDuration(0); + var labelInterval = explicitLabelInterval || computeLabelInterval(slotDuration); + var metas = []; + while (asRoughMs(slatTime) < asRoughMs(slotMaxTime)) { + var date = dateEnv.add(dayStart, slatTime); + var isLabeled = wholeDivideDurations(slatIterator, labelInterval) !== null; + metas.push({ + date: date, + time: slatTime, + key: date.toISOString(), + isoTimeStr: formatIsoTimeString(date), + isLabeled: isLabeled + }); + slatTime = addDurations(slatTime, slotDuration); + slatIterator = addDurations(slatIterator, slotDuration); + } + return metas; + } + // Computes an automatic value for slotLabelInterval + function computeLabelInterval(slotDuration) { + var i; + var labelInterval; + var slotsPerLabel; + // find the smallest stock label interval that results in more than one slots-per-label + for (i = STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) { + labelInterval = createDuration(STOCK_SUB_DURATIONS[i]); + slotsPerLabel = wholeDivideDurations(labelInterval, slotDuration); + if (slotsPerLabel !== null && slotsPerLabel > 1) { + return labelInterval; + } + } + return slotDuration; // fall back + } + + var DEFAULT_WEEK_NUM_FORMAT$1 = createFormatter({ week: 'short' }); + var AUTO_ALL_DAY_MAX_EVENT_ROWS = 5; + var TimeColsView = /** @class */ (function (_super) { + __extends(TimeColsView, _super); + function TimeColsView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.allDaySplitter = new AllDaySplitter(); // for use by subclasses + _this.headerElRef = createRef(); + _this.rootElRef = createRef(); + _this.scrollerElRef = createRef(); + _this.state = { + slatCoords: null + }; + _this.handleScrollTopRequest = function (scrollTop) { + var scrollerEl = _this.scrollerElRef.current; + if (scrollerEl) { // TODO: not sure how this could ever be null. weirdness with the reducer + scrollerEl.scrollTop = scrollTop; + } + }; + /* Header Render Methods + ------------------------------------------------------------------------------------------------------------------*/ + _this.renderHeadAxis = function (frameHeight) { + if (frameHeight === void 0) { frameHeight = ''; } + var options = _this.context.options; + var dateProfile = _this.props.dateProfile; + var range = dateProfile.renderRange; + var dayCnt = diffDays(range.start, range.end); + var navLinkAttrs = (options.navLinks && dayCnt === 1) // only do in day views (to avoid doing in week views that dont need it) + ? { 'data-navlink': buildNavLinkData(range.start, 'week'), tabIndex: 0 } + : {}; + if (options.weekNumbers) { + return (createElement(WeekNumberRoot, { date: range.start, defaultFormat: DEFAULT_WEEK_NUM_FORMAT$1 }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("th", { ref: rootElRef, className: [ + 'fc-timegrid-axis', + 'fc-scrollgrid-shrink' + ].concat(classNames).join(' ') }, + createElement("div", { className: 'fc-timegrid-axis-frame fc-scrollgrid-shrink-frame fc-timegrid-axis-frame-liquid', style: { height: frameHeight } }, + createElement("a", __assign({ ref: innerElRef, className: 'fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner' }, navLinkAttrs), innerContent)))); })); + } + return (createElement("th", { className: 'fc-timegrid-axis' }, + createElement("div", { className: 'fc-timegrid-axis-frame', style: { height: frameHeight } }))); + }; + /* Table Component Render Methods + ------------------------------------------------------------------------------------------------------------------*/ + // only a one-way height sync. we don't send the axis inner-content height to the DayGrid, + // but DayGrid still needs to have classNames on inner elements in order to measure. + _this.renderTableRowAxis = function (rowHeight) { + var _a = _this.context, options = _a.options, viewApi = _a.viewApi; + var hookProps = { + text: options.allDayText, + view: viewApi + }; + return ( + // TODO: make reusable hook. used in list view too + createElement(RenderHook, { hookProps: hookProps, classNames: options.allDayClassNames, content: options.allDayContent, defaultContent: renderAllDayInner, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: [ + 'fc-timegrid-axis', + 'fc-scrollgrid-shrink' + ].concat(classNames).join(' ') }, + createElement("div", { className: 'fc-timegrid-axis-frame fc-scrollgrid-shrink-frame' + (rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : ''), style: { height: rowHeight } }, + createElement("span", { className: 'fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner', ref: innerElRef }, innerContent)))); })); + }; + _this.handleSlatCoords = function (slatCoords) { + _this.setState({ slatCoords: slatCoords }); + }; + return _this; + } + // rendering + // ---------------------------------------------------------------------------------------------------- + TimeColsView.prototype.renderSimpleLayout = function (headerRowContent, allDayContent, timeContent) { + var _a = this, context = _a.context, props = _a.props; + var sections = []; + var stickyHeaderDates = getStickyHeaderDates(context.options); + if (headerRowContent) { + sections.push({ + type: 'header', + key: 'header', + isSticky: stickyHeaderDates, + chunk: { + elRef: this.headerElRef, + tableClassName: 'fc-col-header', + rowContent: headerRowContent + } + }); + } + if (allDayContent) { + sections.push({ + type: 'body', + key: 'all-day', + chunk: { content: allDayContent } + }); + sections.push({ + type: 'body', + key: 'all-day-divider', + outerContent: ( // TODO: rename to cellContent so don't need to define ? + createElement("tr", { className: 'fc-scrollgrid-section' }, + createElement("td", { className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))) + }); + } + sections.push({ + type: 'body', + key: 'body', + liquid: true, + expandRows: Boolean(context.options.expandRows), + chunk: { + scrollerElRef: this.scrollerElRef, + content: timeContent + } + }); + return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.rootElRef }, function (rootElRef, classNames) { return (createElement("div", { className: ['fc-timegrid'].concat(classNames).join(' '), ref: rootElRef }, + createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, cols: [{ width: 'shrink' }], sections: sections }))); })); + }; + TimeColsView.prototype.renderHScrollLayout = function (headerRowContent, allDayContent, timeContent, colCnt, dayMinWidth, slatMetas, slatCoords // yuck + ) { + var _this = this; + var ScrollGrid = this.context.pluginHooks.scrollGridImpl; + if (!ScrollGrid) { + throw new Error('No ScrollGrid implementation'); + } + var _a = this, context = _a.context, props = _a.props; + var stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options); + var stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options); + var sections = []; + if (headerRowContent) { + sections.push({ + type: 'header', + key: 'header', + isSticky: stickyHeaderDates, + syncRowHeights: true, + chunks: [ + { + key: 'axis', + rowContent: function (arg) { return (createElement("tr", null, _this.renderHeadAxis(arg.rowSyncHeights[0]))); } + }, + { + key: 'cols', + elRef: this.headerElRef, + tableClassName: 'fc-col-header', + rowContent: headerRowContent + } + ] + }); + } + if (allDayContent) { + sections.push({ + type: 'body', + key: 'all-day', + syncRowHeights: true, + chunks: [ + { + key: 'axis', + rowContent: function (contentArg) { return (createElement("tr", null, _this.renderTableRowAxis(contentArg.rowSyncHeights[0]))); }, + }, + { + key: 'cols', + content: allDayContent + } + ] + }); + sections.push({ + key: 'all-day-divider', + type: 'body', + outerContent: ( // TODO: rename to cellContent so don't need to define ? + createElement("tr", { className: 'fc-scrollgrid-section' }, + createElement("td", { colSpan: 2, className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))) + }); + } + var isNowIndicator = context.options.nowIndicator; + sections.push({ + type: 'body', + key: 'body', + liquid: true, + expandRows: Boolean(context.options.expandRows), + chunks: [ + { + key: 'axis', + content: function (arg) { + // TODO: make this now-indicator arrow more DRY with TimeColsContent + return (createElement("div", { className: 'fc-timegrid-axis-chunk' }, + createElement("table", { style: { height: arg.expandRows ? arg.clientHeight : '' } }, + arg.tableColGroupNode, + createElement("tbody", null, + createElement(TimeBodyAxis, { slatMetas: slatMetas }))), + createElement("div", { className: 'fc-timegrid-now-indicator-container' }, + createElement(NowTimer, { unit: isNowIndicator ? 'minute' : 'day' /* hacky */ }, function (nowDate) { + var nowIndicatorTop = isNowIndicator && + slatCoords && + slatCoords.safeComputeTop(nowDate); // might return void + if (typeof nowIndicatorTop === 'number') { + return (createElement(NowIndicatorRoot, { isAxis: true, date: nowDate }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-arrow'].concat(classNames).join(' '), style: { top: nowIndicatorTop } }, innerContent)); })); + } + return null; + })))); + } + }, + { + key: 'cols', + scrollerElRef: this.scrollerElRef, + content: timeContent + } + ] + }); + if (stickyFooterScrollbar) { + sections.push({ + key: 'footer', + type: 'footer', + isSticky: true, + chunks: [ + { + key: 'axis', + content: renderScrollShim + }, + { + key: 'cols', + content: renderScrollShim + } + ] + }); + } + return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.rootElRef }, function (rootElRef, classNames) { return (createElement("div", { className: ['fc-timegrid'].concat(classNames).join(' '), ref: rootElRef }, + createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, colGroups: [ + { width: 'shrink', cols: [{ width: 'shrink' }] }, + { cols: [{ span: colCnt, minWidth: dayMinWidth }] } + ], sections: sections }))); })); + }; + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + TimeColsView.prototype.getAllDayMaxEventProps = function () { + var _a = this.context.options, dayMaxEvents = _a.dayMaxEvents, dayMaxEventRows = _a.dayMaxEventRows; + if (dayMaxEvents === true || dayMaxEventRows === true) { // is auto? + dayMaxEvents = undefined; + dayMaxEventRows = AUTO_ALL_DAY_MAX_EVENT_ROWS; // make sure "auto" goes to a real number + } + return { dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows }; + }; + return TimeColsView; + }(DateComponent)); + function renderAllDayInner(hookProps) { + return hookProps.text; + } + var TimeBodyAxis = /** @class */ (function (_super) { + __extends(TimeBodyAxis, _super); + function TimeBodyAxis() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeBodyAxis.prototype.render = function () { + return this.props.slatMetas.map(function (slatMeta) { return (createElement("tr", { key: slatMeta.key }, + createElement(TimeColsAxisCell, __assign({}, slatMeta)))); }); + }; + return TimeBodyAxis; + }(BaseComponent)); + + function splitSegsByCol(segs, colCnt) { + var segsByCol = []; + var i; + for (i = 0; i < colCnt; i++) { + segsByCol.push([]); + } + if (segs) { + for (i = 0; i < segs.length; i++) { + segsByCol[segs[i].col].push(segs[i]); + } + } + return segsByCol; + } + function splitInteractionByCol(ui, colCnt) { + var byRow = []; + if (!ui) { + for (var i = 0; i < colCnt; i++) { + byRow[i] = null; + } + } + else { + for (var i = 0; i < colCnt; i++) { + byRow[i] = { + affectedInstances: ui.affectedInstances, + isEvent: ui.isEvent, + segs: [] + }; + } + for (var _i = 0, _a = ui.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + byRow[seg.col].segs.push(seg); + } + } + return byRow; + } + + // UNFORTUNATELY, assigns results to the top/bottom/level/forwardCoord/backwardCoord props of the actual segs. + // TODO: return hash (by instanceId) of results + function computeSegCoords(segs, dayDate, slatCoords, eventMinHeight, eventOrderSpecs) { + computeSegVerticals(segs, dayDate, slatCoords, eventMinHeight); + return computeSegHorizontals(segs, eventOrderSpecs); // requires top/bottom from computeSegVerticals + } + // For each segment in an array, computes and assigns its top and bottom properties + function computeSegVerticals(segs, dayDate, slatCoords, eventMinHeight) { + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + seg.top = slatCoords.computeDateTop(seg.start, dayDate); + seg.bottom = Math.max(seg.top + (eventMinHeight || 0), // yuck + slatCoords.computeDateTop(seg.end, dayDate)); + } + } + // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each. + // Assumed the segs are already ordered. + // NOTE: Also reorders the given array by date! + function computeSegHorizontals(segs, eventOrderSpecs) { + // IMPORTANT TO CLEAR OLD RESULTS :( + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + seg.level = null; + seg.forwardCoord = null; + seg.backwardCoord = null; + seg.forwardPressure = null; + } + segs = sortEventSegs(segs, eventOrderSpecs); + var level0; + var levels = buildSlotSegLevels(segs); + computeForwardSlotSegs(levels); + if ((level0 = levels[0])) { + for (var _a = 0, level0_1 = level0; _a < level0_1.length; _a++) { + var seg = level0_1[_a]; + computeSlotSegPressures(seg); + } + for (var _b = 0, level0_2 = level0; _b < level0_2.length; _b++) { + var seg = level0_2[_b]; + computeSegForwardBack(seg, 0, 0, eventOrderSpecs); + } + } + return segs; + } + // Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is + // left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date. + function buildSlotSegLevels(segs) { + var levels = []; + var i; + var seg; + var j; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + // go through all the levels and stop on the first level where there are no collisions + for (j = 0; j < levels.length; j++) { + if (!computeSlotSegCollisions(seg, levels[j]).length) { + break; + } + } + seg.level = j; + (levels[j] || (levels[j] = [])).push(seg); + } + return levels; + } + // Find all the segments in `otherSegs` that vertically collide with `seg`. + // Append into an optionally-supplied `results` array and return. + function computeSlotSegCollisions(seg, otherSegs, results) { + if (results === void 0) { results = []; } + for (var i = 0; i < otherSegs.length; i++) { + if (isSlotSegCollision(seg, otherSegs[i])) { + results.push(otherSegs[i]); + } + } + return results; + } + // Do these segments occupy the same vertical space? + function isSlotSegCollision(seg1, seg2) { + return seg1.bottom > seg2.top && seg1.top < seg2.bottom; + } + // For every segment, figure out the other segments that are in subsequent + // levels that also occupy the same vertical space. Accumulate in seg.forwardSegs + function computeForwardSlotSegs(levels) { + var i; + var level; + var j; + var seg; + var k; + for (i = 0; i < levels.length; i++) { + level = levels[i]; + for (j = 0; j < level.length; j++) { + seg = level[j]; + seg.forwardSegs = []; + for (k = i + 1; k < levels.length; k++) { + computeSlotSegCollisions(seg, levels[k], seg.forwardSegs); + } + } + } + } + // Figure out which path forward (via seg.forwardSegs) results in the longest path until + // the furthest edge is reached. The number of segments in this path will be seg.forwardPressure + function computeSlotSegPressures(seg) { + var forwardSegs = seg.forwardSegs; + var forwardPressure = 0; + var i; + var forwardSeg; + if (seg.forwardPressure == null) { // not already computed + for (i = 0; i < forwardSegs.length; i++) { + forwardSeg = forwardSegs[i]; + // figure out the child's maximum forward path + computeSlotSegPressures(forwardSeg); + // either use the existing maximum, or use the child's forward pressure + // plus one (for the forwardSeg itself) + forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure); + } + seg.forwardPressure = forwardPressure; + } + } + // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range + // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and + // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left. + // + // The segment might be part of a "series", which means consecutive segments with the same pressure + // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of + // segments behind this one in the current series, and `seriesBackwardCoord` is the starting + // coordinate of the first segment in the series. + function computeSegForwardBack(seg, seriesBackwardPressure, seriesBackwardCoord, eventOrderSpecs) { + var forwardSegs = seg.forwardSegs; + var i; + if (seg.forwardCoord == null) { // not already computed + if (!forwardSegs.length) { + // if there are no forward segments, this segment should butt up against the edge + seg.forwardCoord = 1; + } + else { + // sort highest pressure first + sortForwardSegs(forwardSegs, eventOrderSpecs); + // this segment's forwardCoord will be calculated from the backwardCoord of the + // highest-pressure forward segment. + computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord, eventOrderSpecs); + seg.forwardCoord = forwardSegs[0].backwardCoord; + } + // calculate the backwardCoord from the forwardCoord. consider the series + seg.backwardCoord = seg.forwardCoord - + (seg.forwardCoord - seriesBackwardCoord) / // available width for series + (seriesBackwardPressure + 1); // # of segments in the series + // use this segment's coordinates to computed the coordinates of the less-pressurized + // forward segments + for (i = 0; i < forwardSegs.length; i++) { + computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord, eventOrderSpecs); + } + } + } + function sortForwardSegs(forwardSegs, eventOrderSpecs) { + var objs = forwardSegs.map(buildTimeGridSegCompareObj); + var specs = [ + // put higher-pressure first + { field: 'forwardPressure', order: -1 }, + // put segments that are closer to initial edge first (and favor ones with no coords yet) + { field: 'backwardCoord', order: 1 } + ].concat(eventOrderSpecs); + objs.sort(function (obj0, obj1) { + return compareByFieldSpecs(obj0, obj1, specs); + }); + return objs.map(function (c) { + return c._seg; + }); + } + function buildTimeGridSegCompareObj(seg) { + var obj = buildSegCompareObj(seg); + obj.forwardPressure = seg.forwardPressure; + obj.backwardCoord = seg.backwardCoord; + return obj; + } + + var DEFAULT_TIME_FORMAT = createFormatter({ + hour: 'numeric', + minute: '2-digit', + meridiem: false + }); + var TimeColEvent = /** @class */ (function (_super) { + __extends(TimeColEvent, _super); + function TimeColEvent() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeColEvent.prototype.render = function () { + var classNames = [ + 'fc-timegrid-event', + 'fc-v-event' + ]; + if (this.props.isCondensed) { + classNames.push('fc-timegrid-event-condensed'); + } + return (createElement(StandardEvent, __assign({}, this.props, { defaultTimeFormat: DEFAULT_TIME_FORMAT, extraClassNames: classNames }))); + }; + return TimeColEvent; + }(BaseComponent)); + + config.timeGridEventCondensedHeight = 30; + var TimeCol = /** @class */ (function (_super) { + __extends(TimeCol, _super); + function TimeCol() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeCol.prototype.render = function () { + var _this = this; + var _a = this, props = _a.props, context = _a.context; + var isSelectMirror = context.options.selectMirror; + var mirrorSegs = (props.eventDrag && props.eventDrag.segs) || + (props.eventResize && props.eventResize.segs) || + (isSelectMirror && props.dateSelectionSegs) || + []; + var interactionAffectedInstances = // TODO: messy way to compute this + (props.eventDrag && props.eventDrag.affectedInstances) || + (props.eventResize && props.eventResize.affectedInstances) || + {}; + return (createElement(DayCellRoot, { elRef: props.elRef, date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps }, function (rootElRef, classNames, dataAttrs) { return (createElement("td", __assign({ ref: rootElRef, className: ['fc-timegrid-col'].concat(classNames, props.extraClassNames || []).join(' ') }, dataAttrs, props.extraDataAttrs), + createElement("div", { className: 'fc-timegrid-col-frame' }, + createElement("div", { className: 'fc-timegrid-col-bg' }, + _this.renderFillSegs(props.businessHourSegs, 'non-business'), + _this.renderFillSegs(props.bgEventSegs, 'bg-event'), + _this.renderFillSegs(props.dateSelectionSegs, 'highlight')), + createElement("div", { className: 'fc-timegrid-col-events' }, _this.renderFgSegs(props.fgEventSegs, interactionAffectedInstances)), + createElement("div", { className: 'fc-timegrid-col-events' }, _this.renderFgSegs(mirrorSegs, {}, Boolean(props.eventDrag), Boolean(props.eventResize), Boolean(isSelectMirror) + // TODO: pass in left/right instead of using only computeSegTopBottomCss + )), + createElement("div", { className: 'fc-timegrid-now-indicator-container' }, _this.renderNowIndicator(props.nowIndicatorSegs)), + createElement(TimeColMisc, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps })))); })); + }; + TimeCol.prototype.renderFgSegs = function (segs, segIsInvisible, isDragging, isResizing, isDateSelecting) { + var props = this.props; + if (props.forPrint) { + return this.renderPrintFgSegs(segs); + } + else if (props.slatCoords) { + return this.renderPositionedFgSegs(segs, segIsInvisible, isDragging, isResizing, isDateSelecting); + } + }; + TimeCol.prototype.renderPrintFgSegs = function (segs) { + var _a = this, props = _a.props, context = _a.context; + // not DRY + segs = sortEventSegs(segs, context.options.eventOrder); + return segs.map(function (seg) { return (createElement("div", { className: 'fc-timegrid-event-harness', key: seg.eventRange.instance.instanceId }, + createElement(TimeColEvent, __assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isCondensed: false }, getSegMeta(seg, props.todayRange, props.nowDate))))); }); + }; + TimeCol.prototype.renderPositionedFgSegs = function (segs, segIsInvisible, isDragging, isResizing, isDateSelecting) { + var _this = this; + var _a = this, context = _a.context, props = _a.props; + // assigns TO THE SEGS THEMSELVES + // also, receives resorted array + segs = computeSegCoords(segs, props.date, props.slatCoords, context.options.eventMinHeight, context.options.eventOrder); + return segs.map(function (seg) { + var instanceId = seg.eventRange.instance.instanceId; + var isMirror = isDragging || isResizing || isDateSelecting; + var positionCss = isMirror ? __assign({ left: 0, right: 0 }, _this.computeSegTopBottomCss(seg)) : + _this.computeFgSegPositionCss(seg); + return (createElement("div", { className: 'fc-timegrid-event-harness' + (seg.level > 0 ? ' fc-timegrid-event-harness-inset' : ''), key: instanceId, style: __assign({ visibility: segIsInvisible[instanceId] ? 'hidden' : '' }, positionCss) }, + createElement(TimeColEvent, __assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection, isCondensed: (seg.bottom - seg.top) < config.timeGridEventCondensedHeight }, getSegMeta(seg, props.todayRange, props.nowDate))))); + }); + }; + TimeCol.prototype.renderFillSegs = function (segs, fillType) { + var _this = this; + var _a = this, context = _a.context, props = _a.props; + if (!props.slatCoords) { + return; + } + // BAD: assigns TO THE SEGS THEMSELVES + computeSegVerticals(segs, props.date, props.slatCoords, context.options.eventMinHeight); + var children = segs.map(function (seg) { return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: 'fc-timegrid-bg-harness', style: _this.computeSegTopBottomCss(seg) }, fillType === 'bg-event' ? + createElement(BgEvent, __assign({ seg: seg }, getSegMeta(seg, props.todayRange, props.nowDate))) : + renderFill(fillType))); }); + return createElement(Fragment, null, children); + }; + TimeCol.prototype.renderNowIndicator = function (segs) { + var _a = this.props, slatCoords = _a.slatCoords, date = _a.date; + if (!slatCoords) { + return; + } + return segs.map(function (seg, i) { return (createElement(NowIndicatorRoot, { isAxis: false, date: date, key: i /* key doesn't matter. will only ever be one */ }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-line'].concat(classNames).join(' '), style: { top: slatCoords.computeDateTop(seg.start, date) } }, innerContent)); })); }); + }; + TimeCol.prototype.computeFgSegPositionCss = function (seg) { + var _a = this.context, isRtl = _a.isRtl, options = _a.options; + var shouldOverlap = options.slotEventOverlap; + var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point + var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point + var left; // amount of space from left edge, a fraction of the total width + var right; // amount of space from right edge, a fraction of the total width + if (shouldOverlap) { + // double the width, but don't go beyond the maximum forward coordinate (1.0) + forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2); + } + if (isRtl) { + left = 1 - forwardCoord; + right = backwardCoord; + } + else { + left = backwardCoord; + right = 1 - forwardCoord; + } + var props = { + zIndex: seg.level + 1, + left: left * 100 + '%', + right: right * 100 + '%' + }; + if (shouldOverlap && seg.forwardPressure) { + // add padding to the edge so that forward stacked events don't cover the resizer's icon + props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width + } + return __assign(__assign({}, props), this.computeSegTopBottomCss(seg)); + }; + TimeCol.prototype.computeSegTopBottomCss = function (seg) { + return { + top: seg.top, + bottom: -seg.bottom + }; + }; + return TimeCol; + }(BaseComponent)); + var TimeColMisc = /** @class */ (function (_super) { + __extends(TimeColMisc, _super); + function TimeColMisc() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeColMisc.prototype.render = function () { + var props = this.props; + return (createElement(DayCellContent, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps }, function (innerElRef, innerContent) { return (innerContent && + createElement("div", { className: 'fc-timegrid-col-misc', ref: innerElRef }, innerContent)); })); + }; + return TimeColMisc; + }(BaseComponent)); + + var TimeColsContent = /** @class */ (function (_super) { + __extends(TimeColsContent, _super); + function TimeColsContent() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.splitFgEventSegs = memoize(splitSegsByCol); + _this.splitBgEventSegs = memoize(splitSegsByCol); + _this.splitBusinessHourSegs = memoize(splitSegsByCol); + _this.splitNowIndicatorSegs = memoize(splitSegsByCol); + _this.splitDateSelectionSegs = memoize(splitSegsByCol); + _this.splitEventDrag = memoize(splitInteractionByCol); + _this.splitEventResize = memoize(splitInteractionByCol); + _this.rootElRef = createRef(); + _this.cellElRefs = new RefMap(); + return _this; + } + TimeColsContent.prototype.render = function () { + var _this = this; + var _a = this, props = _a.props, context = _a.context; + var nowIndicatorTop = context.options.nowIndicator && + props.slatCoords && + props.slatCoords.safeComputeTop(props.nowDate); // might return void + var colCnt = props.cells.length; + var fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, colCnt); + var bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, colCnt); + var businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, colCnt); + var nowIndicatorSegsByRow = this.splitNowIndicatorSegs(props.nowIndicatorSegs, colCnt); + var dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, colCnt); + var eventDragByRow = this.splitEventDrag(props.eventDrag, colCnt); + var eventResizeByRow = this.splitEventResize(props.eventResize, colCnt); + return (createElement("div", { className: 'fc-timegrid-cols', ref: this.rootElRef }, + createElement("table", { style: { + minWidth: props.tableMinWidth, + width: props.clientWidth + } }, + props.tableColGroupNode, + createElement("tbody", null, + createElement("tr", null, + props.axis && + createElement("td", { className: 'fc-timegrid-col fc-timegrid-axis' }, + createElement("div", { className: 'fc-timegrid-col-frame' }, + createElement("div", { className: 'fc-timegrid-now-indicator-container' }, typeof nowIndicatorTop === 'number' && + createElement(NowIndicatorRoot, { isAxis: true, date: props.nowDate }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-arrow'].concat(classNames).join(' '), style: { top: nowIndicatorTop } }, innerContent)); })))), + props.cells.map(function (cell, i) { return (createElement(TimeCol, { key: cell.key, elRef: _this.cellElRefs.createRef(cell.key), dateProfile: props.dateProfile, date: cell.date, nowDate: props.nowDate, todayRange: props.todayRange, extraHookProps: cell.extraHookProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, fgEventSegs: fgEventSegsByRow[i], bgEventSegs: bgEventSegsByRow[i], businessHourSegs: businessHourSegsByRow[i], nowIndicatorSegs: nowIndicatorSegsByRow[i], dateSelectionSegs: dateSelectionSegsByRow[i], eventDrag: eventDragByRow[i], eventResize: eventResizeByRow[i], slatCoords: props.slatCoords, eventSelection: props.eventSelection, forPrint: props.forPrint })); })))))); + }; + TimeColsContent.prototype.componentDidMount = function () { + this.updateCoords(); + }; + TimeColsContent.prototype.componentDidUpdate = function () { + this.updateCoords(); + }; + TimeColsContent.prototype.updateCoords = function () { + var props = this.props; + if (props.onColCoords && + props.clientWidth !== null // means sizing has stabilized + ) { + props.onColCoords(new PositionCache(this.rootElRef.current, collectCellEls(this.cellElRefs.currentMap, props.cells), true, // horizontal + false)); + } + }; + return TimeColsContent; + }(BaseComponent)); + function collectCellEls(elMap, cells) { + return cells.map(function (cell) { return elMap[cell.key]; }); + } + + /* A component that renders one or more columns of vertical time slots + ----------------------------------------------------------------------------------------------------------------------*/ + var TimeCols = /** @class */ (function (_super) { + __extends(TimeCols, _super); + function TimeCols() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.processSlotOptions = memoize(processSlotOptions); + _this.state = { + slatCoords: null + }; + _this.handleScrollRequest = function (request) { + var onScrollTopRequest = _this.props.onScrollTopRequest; + var slatCoords = _this.state.slatCoords; + if (onScrollTopRequest && slatCoords) { + if (request.time) { + var top_1 = slatCoords.computeTimeTop(request.time); + top_1 = Math.ceil(top_1); // zoom can give weird floating-point values. rather scroll a little bit further + if (top_1) { + top_1++; + } // to overcome top border that slots beyond the first have. looks better + onScrollTopRequest(top_1); + } + return true; + } + }; + _this.handleColCoords = function (colCoords) { + _this.colCoords = colCoords; + }; + _this.handleSlatCoords = function (slatCoords) { + _this.setState({ slatCoords: slatCoords }); + if (_this.props.onSlatCoords) { + _this.props.onSlatCoords(slatCoords); + } + }; + return _this; + } + TimeCols.prototype.render = function () { + var _a = this, props = _a.props, state = _a.state; + return (createElement("div", { className: 'fc-timegrid-body', ref: props.rootElRef, style: { + // these props are important to give this wrapper correct dimensions for interactions + // TODO: if we set it here, can we avoid giving to inner tables? + width: props.clientWidth, + minWidth: props.tableMinWidth + } }, + createElement(TimeColsSlats, { axis: props.axis, dateProfile: props.dateProfile, slatMetas: props.slatMetas, clientWidth: props.clientWidth, minHeight: props.expandRows ? props.clientHeight : '', tableMinWidth: props.tableMinWidth, tableColGroupNode: props.axis ? props.tableColGroupNode : null /* axis depends on the colgroup's shrinking */, onCoords: this.handleSlatCoords }), + createElement(TimeColsContent, { cells: props.cells, axis: props.axis, dateProfile: props.dateProfile, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange, nowDate: props.nowDate, nowIndicatorSegs: props.nowIndicatorSegs, clientWidth: props.clientWidth, tableMinWidth: props.tableMinWidth, tableColGroupNode: props.tableColGroupNode, slatCoords: state.slatCoords, onColCoords: this.handleColCoords, forPrint: props.forPrint }))); + }; + TimeCols.prototype.componentDidMount = function () { + this.scrollResponder = this.context.createScrollResponder(this.handleScrollRequest); + }; + TimeCols.prototype.componentDidUpdate = function (prevProps) { + this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile); + }; + TimeCols.prototype.componentWillUnmount = function () { + this.scrollResponder.detach(); + }; + TimeCols.prototype.positionToHit = function (positionLeft, positionTop) { + var _a = this.context, dateEnv = _a.dateEnv, options = _a.options; + var colCoords = this.colCoords; + var dateProfile = this.props.dateProfile; + var slatCoords = this.state.slatCoords; + var _b = this.processSlotOptions(this.props.slotDuration, options.snapDuration), snapDuration = _b.snapDuration, snapsPerSlot = _b.snapsPerSlot; + var colIndex = colCoords.leftToIndex(positionLeft); + var slatIndex = slatCoords.positions.topToIndex(positionTop); + if (colIndex != null && slatIndex != null) { + var slatTop = slatCoords.positions.tops[slatIndex]; + var slatHeight = slatCoords.positions.getHeight(slatIndex); + var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1 + var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat + var snapIndex = slatIndex * snapsPerSlot + localSnapIndex; + var dayDate = this.props.cells[colIndex].date; + var time = addDurations(dateProfile.slotMinTime, multiplyDuration(snapDuration, snapIndex)); + var start = dateEnv.add(dayDate, time); + var end = dateEnv.add(start, snapDuration); + return { + col: colIndex, + dateSpan: { + range: { start: start, end: end }, + allDay: false + }, + dayEl: colCoords.els[colIndex], + relativeRect: { + left: colCoords.lefts[colIndex], + right: colCoords.rights[colIndex], + top: slatTop, + bottom: slatTop + slatHeight + } + }; + } + }; + return TimeCols; + }(BaseComponent)); + function processSlotOptions(slotDuration, snapDurationOverride) { + var snapDuration = snapDurationOverride || slotDuration; + var snapsPerSlot = wholeDivideDurations(slotDuration, snapDuration); + if (snapsPerSlot === null) { + snapDuration = slotDuration; + snapsPerSlot = 1; + // TODO: say warning? + } + return { snapDuration: snapDuration, snapsPerSlot: snapsPerSlot }; + } + + var DayTimeCols = /** @class */ (function (_super) { + __extends(DayTimeCols, _super); + function DayTimeCols() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.buildDayRanges = memoize(buildDayRanges); + _this.slicer = new DayTimeColsSlicer(); + _this.timeColsRef = createRef(); + _this.handleRootEl = function (rootEl) { + if (rootEl) { + _this.context.registerInteractiveComponent(_this, { el: rootEl }); + } + else { + _this.context.unregisterInteractiveComponent(_this); + } + }; + return _this; + } + DayTimeCols.prototype.render = function () { + var _this = this; + var _a = this, props = _a.props, context = _a.context; + var dateProfile = props.dateProfile, dayTableModel = props.dayTableModel; + var isNowIndicator = context.options.nowIndicator; + var dayRanges = this.buildDayRanges(dayTableModel, dateProfile, context.dateEnv); + // give it the first row of cells + // TODO: would move this further down hierarchy, but sliceNowDate needs it + return (createElement(NowTimer, { unit: isNowIndicator ? 'minute' : 'day' }, function (nowDate, todayRange) { return (createElement(TimeCols, __assign({ ref: _this.timeColsRef, rootElRef: _this.handleRootEl }, _this.slicer.sliceProps(props, dateProfile, null, context, dayRanges), { forPrint: props.forPrint, axis: props.axis, dateProfile: dateProfile, slatMetas: props.slatMetas, slotDuration: props.slotDuration, cells: dayTableModel.cells[0], tableColGroupNode: props.tableColGroupNode, tableMinWidth: props.tableMinWidth, clientWidth: props.clientWidth, clientHeight: props.clientHeight, expandRows: props.expandRows, nowDate: nowDate, nowIndicatorSegs: isNowIndicator && _this.slicer.sliceNowDate(nowDate, context, dayRanges), todayRange: todayRange, onScrollTopRequest: props.onScrollTopRequest, onSlatCoords: props.onSlatCoords }))); })); + }; + DayTimeCols.prototype.queryHit = function (positionLeft, positionTop) { + var rawHit = this.timeColsRef.current.positionToHit(positionLeft, positionTop); + if (rawHit) { + return { + component: this, + dateSpan: rawHit.dateSpan, + dayEl: rawHit.dayEl, + rect: { + left: rawHit.relativeRect.left, + right: rawHit.relativeRect.right, + top: rawHit.relativeRect.top, + bottom: rawHit.relativeRect.bottom + }, + layer: 0 + }; + } + }; + return DayTimeCols; + }(DateComponent)); + function buildDayRanges(dayTableModel, dateProfile, dateEnv) { + var ranges = []; + for (var _i = 0, _a = dayTableModel.headerDates; _i < _a.length; _i++) { + var date = _a[_i]; + ranges.push({ + start: dateEnv.add(date, dateProfile.slotMinTime), + end: dateEnv.add(date, dateProfile.slotMaxTime) + }); + } + return ranges; + } + var DayTimeColsSlicer = /** @class */ (function (_super) { + __extends(DayTimeColsSlicer, _super); + function DayTimeColsSlicer() { + return _super !== null && _super.apply(this, arguments) || this; + } + DayTimeColsSlicer.prototype.sliceRange = function (range, dayRanges) { + var segs = []; + for (var col = 0; col < dayRanges.length; col++) { + var segRange = intersectRanges(range, dayRanges[col]); + if (segRange) { + segs.push({ + start: segRange.start, + end: segRange.end, + isStart: segRange.start.valueOf() === range.start.valueOf(), + isEnd: segRange.end.valueOf() === range.end.valueOf(), + col: col + }); + } + } + return segs; + }; + return DayTimeColsSlicer; + }(Slicer)); + + var DayTimeColsView = /** @class */ (function (_super) { + __extends(DayTimeColsView, _super); + function DayTimeColsView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.buildTimeColsModel = memoize(buildTimeColsModel); + _this.buildSlatMetas = memoize(buildSlatMetas); + return _this; + } + DayTimeColsView.prototype.render = function () { + var _this = this; + var _a = this.context, options = _a.options, dateEnv = _a.dateEnv, dateProfileGenerator = _a.dateProfileGenerator; + var props = this.props; + var dateProfile = props.dateProfile; + var dayTableModel = this.buildTimeColsModel(dateProfile, dateProfileGenerator); + var splitProps = this.allDaySplitter.splitProps(props); + var slatMetas = this.buildSlatMetas(dateProfile.slotMinTime, dateProfile.slotMaxTime, options.slotLabelInterval, options.slotDuration, dateEnv); + var dayMinWidth = options.dayMinWidth; + var hasAttachedAxis = !dayMinWidth; + var hasDetachedAxis = dayMinWidth; + var headerContent = options.dayHeaders && + createElement(DayHeader, { dates: dayTableModel.headerDates, dateProfile: dateProfile, datesRepDistinctDays: true, renderIntro: hasAttachedAxis ? this.renderHeadAxis : null }); + var allDayContent = (options.allDaySlot !== false) && (function (contentArg) { return (createElement(DayTable, __assign({}, splitProps['allDay'], { dateProfile: dateProfile, dayTableModel: dayTableModel, nextDayThreshold: options.nextDayThreshold, tableMinWidth: contentArg.tableMinWidth, colGroupNode: contentArg.tableColGroupNode, renderRowIntro: hasAttachedAxis ? _this.renderTableRowAxis : null, showWeekNumbers: false, expandRows: false, headerAlignElRef: _this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint }, _this.getAllDayMaxEventProps()))); }); + var timeGridContent = function (contentArg) { return (createElement(DayTimeCols, __assign({}, splitProps['timed'], { dayTableModel: dayTableModel, dateProfile: dateProfile, axis: hasAttachedAxis, slotDuration: options.slotDuration, slatMetas: slatMetas, forPrint: props.forPrint, tableColGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, onSlatCoords: _this.handleSlatCoords, expandRows: contentArg.expandRows, onScrollTopRequest: _this.handleScrollTopRequest }))); }; + return hasDetachedAxis + ? this.renderHScrollLayout(headerContent, allDayContent, timeGridContent, dayTableModel.colCnt, dayMinWidth, slatMetas, this.state.slatCoords) + : this.renderSimpleLayout(headerContent, allDayContent, timeGridContent); + }; + return DayTimeColsView; + }(TimeColsView)); + function buildTimeColsModel(dateProfile, dateProfileGenerator) { + var daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator); + return new DayTableModel(daySeries, false); + } + + var OPTION_REFINERS$1 = { + allDaySlot: Boolean + }; + + var timeGridPlugin = createPlugin({ + initialView: 'timeGridWeek', + optionRefiners: OPTION_REFINERS$1, + views: { + timeGrid: { + component: DayTimeColsView, + usesMinMaxTime: true, + allDaySlot: true, + slotDuration: '00:30:00', + slotEventOverlap: true // a bad name. confused with overlap/constraint system + }, + timeGridDay: { + type: 'timeGrid', + duration: { days: 1 } + }, + timeGridWeek: { + type: 'timeGrid', + duration: { weeks: 1 } + } + } + }); + + var ListViewHeaderRow = /** @class */ (function (_super) { + __extends(ListViewHeaderRow, _super); + function ListViewHeaderRow() { + return _super !== null && _super.apply(this, arguments) || this; + } + ListViewHeaderRow.prototype.render = function () { + var _a = this.props, dayDate = _a.dayDate, todayRange = _a.todayRange; + var _b = this.context, theme = _b.theme, dateEnv = _b.dateEnv, options = _b.options, viewApi = _b.viewApi; + var dayMeta = getDateMeta(dayDate, todayRange); + var text = options.listDayFormat ? dateEnv.format(dayDate, options.listDayFormat) : ''; // will ever be falsy? + var sideText = options.listDaySideFormat ? dateEnv.format(dayDate, options.listDaySideFormat) : ''; // will ever be falsy? also, BAD NAME "alt" + var navLinkData = options.navLinks + ? buildNavLinkData(dayDate) + : null; + var hookProps = __assign({ date: dateEnv.toDate(dayDate), view: viewApi, text: text, + sideText: sideText, + navLinkData: navLinkData }, dayMeta); + var classNames = ['fc-list-day'].concat(getDayClassNames(dayMeta, theme)); + // TODO: make a reusable HOC for dayHeader (used in daygrid/timegrid too) + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInnerContent$4, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("tr", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-date": formatDayString(dayDate) }, + createElement("th", { colSpan: 3 }, + createElement("div", { className: 'fc-list-day-cushion ' + theme.getClass('tableCellShaded'), ref: innerElRef }, innerContent)))); })); + }; + return ListViewHeaderRow; + }(BaseComponent)); + function renderInnerContent$4(props) { + var navLinkAttrs = props.navLinkData // is there a type for this? + ? { 'data-navlink': props.navLinkData, tabIndex: 0 } + : {}; + return (createElement(Fragment, null, + props.text && + createElement("a", __assign({ className: 'fc-list-day-text' }, navLinkAttrs), props.text), + props.sideText && + createElement("a", __assign({ className: 'fc-list-day-side-text' }, navLinkAttrs), props.sideText))); + } + + var DEFAULT_TIME_FORMAT$1 = createFormatter({ + hour: 'numeric', + minute: '2-digit', + meridiem: 'short' + }); + var ListViewEventRow = /** @class */ (function (_super) { + __extends(ListViewEventRow, _super); + function ListViewEventRow() { + return _super !== null && _super.apply(this, arguments) || this; + } + ListViewEventRow.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + var seg = props.seg; + var timeFormat = context.options.eventTimeFormat || DEFAULT_TIME_FORMAT$1; + return (createElement(EventRoot, { seg: seg, timeText: '' /* BAD. because of all-day content */, disableDragging: true, disableResizing: true, defaultContent: renderEventInnerContent, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday, isSelected: props.isSelected, isDragging: props.isDragging, isResizing: props.isResizing, isDateSelecting: props.isDateSelecting }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("tr", { className: ['fc-list-event', hookProps.event.url ? 'fc-event-forced-url' : ''].concat(classNames).join(' '), ref: rootElRef }, + buildTimeContent(seg, timeFormat, context), + createElement("td", { className: 'fc-list-event-graphic' }, + createElement("span", { className: 'fc-list-event-dot', style: { borderColor: hookProps.borderColor || hookProps.backgroundColor } })), + createElement("td", { className: 'fc-list-event-title', ref: innerElRef }, innerContent))); })); + }; + return ListViewEventRow; + }(BaseComponent)); + function renderEventInnerContent(props) { + var event = props.event; + var url = event.url; + var anchorAttrs = url ? { href: url } : {}; + return (createElement("a", __assign({}, anchorAttrs), event.title)); + } + function buildTimeContent(seg, timeFormat, context) { + var options = context.options; + if (options.displayEventTime !== false) { + var eventDef = seg.eventRange.def; + var eventInstance = seg.eventRange.instance; + var doAllDay = false; + var timeText = void 0; + if (eventDef.allDay) { + doAllDay = true; + } + else if (isMultiDayRange(seg.eventRange.range)) { // TODO: use (!isStart || !isEnd) instead? + if (seg.isStart) { + timeText = buildSegTimeText(seg, timeFormat, context, null, null, eventInstance.range.start, seg.end); + } + else if (seg.isEnd) { + timeText = buildSegTimeText(seg, timeFormat, context, null, null, seg.start, eventInstance.range.end); + } + else { + doAllDay = true; + } + } + else { + timeText = buildSegTimeText(seg, timeFormat, context); + } + if (doAllDay) { + var hookProps = { + text: context.options.allDayText, + view: context.viewApi + }; + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.allDayClassNames, content: options.allDayContent, defaultContent: renderAllDayInner$1, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("td", { className: ['fc-list-event-time'].concat(classNames).join(' '), ref: rootElRef }, innerContent)); })); + } + else { + return (createElement("td", { className: 'fc-list-event-time' }, timeText)); + } + } + return null; + } + function renderAllDayInner$1(hookProps) { + return hookProps.text; + } + + /* + Responsible for the scroller, and forwarding event-related actions into the "grid". + */ + var ListView = /** @class */ (function (_super) { + __extends(ListView, _super); + function ListView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.computeDateVars = memoize(computeDateVars); + _this.eventStoreToSegs = memoize(_this._eventStoreToSegs); + _this.setRootEl = function (rootEl) { + if (rootEl) { + _this.context.registerInteractiveComponent(_this, { + el: rootEl + }); + } + else { + _this.context.unregisterInteractiveComponent(_this); + } + }; + return _this; + } + ListView.prototype.render = function () { + var _this = this; + var _a = this, props = _a.props, context = _a.context; + var extraClassNames = [ + 'fc-list', + context.theme.getClass('table'), + context.options.stickyHeaderDates !== false ? 'fc-list-sticky' : '' + ]; + var _b = this.computeDateVars(props.dateProfile), dayDates = _b.dayDates, dayRanges = _b.dayRanges; + var eventSegs = this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges); + return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.setRootEl }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: extraClassNames.concat(classNames).join(' ') }, + createElement(Scroller, { liquid: !props.isHeightAuto, overflowX: props.isHeightAuto ? 'visible' : 'hidden', overflowY: props.isHeightAuto ? 'visible' : 'auto' }, eventSegs.length > 0 ? + _this.renderSegList(eventSegs, dayDates) : + _this.renderEmptyMessage()))); })); + }; + ListView.prototype.renderEmptyMessage = function () { + var _a = this.context, options = _a.options, viewApi = _a.viewApi; + var hookProps = { + text: options.noEventsText, + view: viewApi + }; + return (createElement(RenderHook, { hookProps: hookProps, classNames: options.noEventsClassNames, content: options.noEventsContent, defaultContent: renderNoEventsInner, didMount: options.noEventsDidMount, willUnmount: options.noEventsWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { className: ['fc-list-empty'].concat(classNames).join(' '), ref: rootElRef }, + createElement("div", { className: 'fc-list-empty-cushion', ref: innerElRef }, innerContent))); })); + }; + ListView.prototype.renderSegList = function (allSegs, dayDates) { + var _a = this.context, theme = _a.theme, options = _a.options; + var segsByDay = groupSegsByDay(allSegs); // sparse array + return (createElement(NowTimer, { unit: 'day' }, function (nowDate, todayRange) { + var innerNodes = []; + for (var dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) { + var daySegs = segsByDay[dayIndex]; + if (daySegs) { // sparse array, so might be undefined + var dayStr = dayDates[dayIndex].toISOString(); + // append a day header + innerNodes.push(createElement(ListViewHeaderRow, { key: dayStr, dayDate: dayDates[dayIndex], todayRange: todayRange })); + daySegs = sortEventSegs(daySegs, options.eventOrder); + for (var _i = 0, daySegs_1 = daySegs; _i < daySegs_1.length; _i++) { + var seg = daySegs_1[_i]; + innerNodes.push(createElement(ListViewEventRow, __assign({ key: dayStr + ':' + seg.eventRange.instance.instanceId /* are multiple segs for an instanceId */, seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false }, getSegMeta(seg, todayRange, nowDate)))); + } + } + } + return (createElement("table", { className: 'fc-list-table ' + theme.getClass('table') }, + createElement("tbody", null, innerNodes))); + })); + }; + ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) { + return this.eventRangesToSegs(sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.options.nextDayThreshold).fg, dayRanges); + }; + ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) { + var segs = []; + for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { + var eventRange = eventRanges_1[_i]; + segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges)); + } + return segs; + }; + ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) { + var dateEnv = this.context.dateEnv; + var nextDayThreshold = this.context.options.nextDayThreshold; + var range = eventRange.range; + var allDay = eventRange.def.allDay; + var dayIndex; + var segRange; + var seg; + var segs = []; + for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) { + segRange = intersectRanges(range, dayRanges[dayIndex]); + if (segRange) { + seg = { + component: this, + eventRange: eventRange, + start: segRange.start, + end: segRange.end, + isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(), + isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(), + dayIndex: dayIndex + }; + segs.push(seg); + // detect when range won't go fully into the next day, + // and mutate the latest seg to the be the end. + if (!seg.isEnd && !allDay && + dayIndex + 1 < dayRanges.length && + range.end < + dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) { + seg.end = range.end; + seg.isEnd = true; + break; + } + } + } + return segs; + }; + return ListView; + }(DateComponent)); + function renderNoEventsInner(hookProps) { + return hookProps.text; + } + function computeDateVars(dateProfile) { + var dayStart = startOfDay(dateProfile.renderRange.start); + var viewEnd = dateProfile.renderRange.end; + var dayDates = []; + var dayRanges = []; + while (dayStart < viewEnd) { + dayDates.push(dayStart); + dayRanges.push({ + start: dayStart, + end: addDays(dayStart, 1) + }); + dayStart = addDays(dayStart, 1); + } + return { dayDates: dayDates, dayRanges: dayRanges }; + } + // Returns a sparse array of arrays, segs grouped by their dayIndex + function groupSegsByDay(segs) { + var segsByDay = []; // sparse array + var i; + var seg; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + (segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = [])) + .push(seg); + } + return segsByDay; + } + + var OPTION_REFINERS$2 = { + listDayFormat: createFalsableFormatter, + listDaySideFormat: createFalsableFormatter, + noEventsClassNames: identity, + noEventsContent: identity, + noEventsDidMount: identity, + noEventsWillUnmount: identity + // noEventsText is defined in base options + }; + function createFalsableFormatter(input) { + return input === false ? null : createFormatter(input); + } + + var listPlugin = createPlugin({ + optionRefiners: OPTION_REFINERS$2, + views: { + list: { + component: ListView, + buttonTextKey: 'list', + listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016" + }, + listDay: { + type: 'list', + duration: { days: 1 }, + listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in headerToolbar + }, + listWeek: { + type: 'list', + duration: { weeks: 1 }, + listDayFormat: { weekday: 'long' }, + listDaySideFormat: { month: 'long', day: 'numeric', year: 'numeric' } + }, + listMonth: { + type: 'list', + duration: { month: 1 }, + listDaySideFormat: { weekday: 'long' } // day-of-week is nice-to-have + }, + listYear: { + type: 'list', + duration: { year: 1 }, + listDaySideFormat: { weekday: 'long' } // day-of-week is nice-to-have + } + } + }); + + var BootstrapTheme = /** @class */ (function (_super) { + __extends(BootstrapTheme, _super); + function BootstrapTheme() { + return _super !== null && _super.apply(this, arguments) || this; + } + return BootstrapTheme; + }(Theme)); + BootstrapTheme.prototype.classes = { + root: 'fc-theme-bootstrap', + table: 'table-bordered', + tableCellShaded: 'table-active', + buttonGroup: 'btn-group', + button: 'btn btn-primary', + buttonActive: 'active', + popover: 'popover', + popoverHeader: 'popover-header', + popoverContent: 'popover-body' + }; + BootstrapTheme.prototype.baseIconClass = 'fa'; + BootstrapTheme.prototype.iconClasses = { + close: 'fa-times', + prev: 'fa-chevron-left', + next: 'fa-chevron-right', + prevYear: 'fa-angle-double-left', + nextYear: 'fa-angle-double-right' + }; + BootstrapTheme.prototype.rtlIconClasses = { + prev: 'fa-chevron-right', + next: 'fa-chevron-left', + prevYear: 'fa-angle-double-right', + nextYear: 'fa-angle-double-left' + }; + BootstrapTheme.prototype.iconOverrideOption = 'bootstrapFontAwesome'; // TODO: make TS-friendly. move the option-processing into this plugin + BootstrapTheme.prototype.iconOverrideCustomButtonOption = 'bootstrapFontAwesome'; + BootstrapTheme.prototype.iconOverridePrefix = 'fa-'; + var plugin = createPlugin({ + themeClasses: { + bootstrap: BootstrapTheme + } + }); + + // rename this file to options.ts like other packages? + var OPTION_REFINERS$3 = { + googleCalendarApiKey: String + }; + + var EVENT_SOURCE_REFINERS$1 = { + googleCalendarApiKey: String, + googleCalendarId: String, + googleCalendarApiBase: String, + extraParams: identity + }; + + // TODO: expose somehow + var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars'; + var eventSourceDef$3 = { + parseMeta: function (refined) { + var googleCalendarId = refined.googleCalendarId; + if (!googleCalendarId && refined.url) { + googleCalendarId = parseGoogleCalendarId(refined.url); + } + if (googleCalendarId) { + return { + googleCalendarId: googleCalendarId, + googleCalendarApiKey: refined.googleCalendarApiKey, + googleCalendarApiBase: refined.googleCalendarApiBase, + extraParams: refined.extraParams + }; + } + return null; + }, + fetch: function (arg, onSuccess, onFailure) { + var _a = arg.context, dateEnv = _a.dateEnv, options = _a.options; + var meta = arg.eventSource.meta; + var apiKey = meta.googleCalendarApiKey || options.googleCalendarApiKey; + if (!apiKey) { + onFailure({ + message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/' + }); + } + else { + var url = buildUrl(meta); + // TODO: make DRY with json-feed-event-source + var extraParams = meta.extraParams; + var extraParamsObj = typeof extraParams === 'function' ? extraParams() : extraParams; + var requestParams_1 = buildRequestParams$1(arg.range, apiKey, extraParamsObj, dateEnv); + requestJson('GET', url, requestParams_1, function (body, xhr) { + if (body.error) { + onFailure({ + message: 'Google Calendar API: ' + body.error.message, + errors: body.error.errors, + xhr: xhr + }); + } + else { + onSuccess({ + rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone), + xhr: xhr + }); + } + }, function (message, xhr) { + onFailure({ message: message, xhr: xhr }); + }); + } + } + }; + function parseGoogleCalendarId(url) { + var match; + // detect if the ID was specified as a single string. + // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars. + if (/^[^/]+@([^/.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) { + return url; + } + else if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^/]*)/.exec(url)) || + (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^/]*)/.exec(url))) { + return decodeURIComponent(match[1]); + } + } + function buildUrl(meta) { + var apiBase = meta.googleCalendarApiBase; + if (!apiBase) { + apiBase = API_BASE; + } + return apiBase + '/' + encodeURIComponent(meta.googleCalendarId) + '/events'; + } + function buildRequestParams$1(range, apiKey, extraParams, dateEnv) { + var params; + var startStr; + var endStr; + if (dateEnv.canComputeOffset) { + // strings will naturally have offsets, which GCal needs + startStr = dateEnv.formatIso(range.start); + endStr = dateEnv.formatIso(range.end); + } + else { + // when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day + // from the UTC day-start to guarantee we're getting all the events + // (start/end will be UTC-coerced dates, so toISOString is okay) + startStr = addDays(range.start, -1).toISOString(); + endStr = addDays(range.end, 1).toISOString(); + } + params = __assign(__assign({}, (extraParams || {})), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 }); + if (dateEnv.timeZone !== 'local') { + params.timeZone = dateEnv.timeZone; + } + return params; + } + function gcalItemsToRawEventDefs(items, gcalTimezone) { + return items.map(function (item) { + return gcalItemToRawEventDef(item, gcalTimezone); + }); + } + function gcalItemToRawEventDef(item, gcalTimezone) { + var url = item.htmlLink || null; + // make the URLs for each event show times in the correct timezone + if (url && gcalTimezone) { + url = injectQsComponent(url, 'ctz=' + gcalTimezone); + } + return { + id: item.id, + title: item.summary, + start: item.start.dateTime || item.start.date, + end: item.end.dateTime || item.end.date, + url: url, + location: item.location, + description: item.description + }; + } + // Injects a string like "arg=value" into the querystring of a URL + // TODO: move to a general util file? + function injectQsComponent(url, component) { + // inject it after the querystring but before the fragment + return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) { + return (qs ? qs + '&' : '?') + component + hash; + }); + } + var googleCalendarPlugin = createPlugin({ + eventSourceDefs: [eventSourceDef$3], + optionRefiners: OPTION_REFINERS$3, + eventSourceRefiners: EVENT_SOURCE_REFINERS$1 + }); + + globalPlugins.push(interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin, plugin, googleCalendarPlugin); + + exports.BASE_OPTION_DEFAULTS = BASE_OPTION_DEFAULTS; + exports.BASE_OPTION_REFINERS = BASE_OPTION_REFINERS; + exports.BaseComponent = BaseComponent; + exports.BgEvent = BgEvent; + exports.BootstrapTheme = BootstrapTheme; + exports.Calendar = Calendar; + exports.CalendarApi = CalendarApi; + exports.CalendarContent = CalendarContent; + exports.CalendarDataManager = CalendarDataManager; + exports.CalendarDataProvider = CalendarDataProvider; + exports.CalendarRoot = CalendarRoot; + exports.Component = Component; + exports.ContentHook = ContentHook; + exports.CustomContentRenderContext = CustomContentRenderContext; + exports.DateComponent = DateComponent; + exports.DateEnv = DateEnv; + exports.DateProfileGenerator = DateProfileGenerator; + exports.DayCellContent = DayCellContent; + exports.DayCellRoot = DayCellRoot; + exports.DayGridView = DayTableView; + exports.DayHeader = DayHeader; + exports.DaySeriesModel = DaySeriesModel; + exports.DayTable = DayTable; + exports.DayTableModel = DayTableModel; + exports.DayTableSlicer = DayTableSlicer; + exports.DayTimeCols = DayTimeCols; + exports.DayTimeColsSlicer = DayTimeColsSlicer; + exports.DayTimeColsView = DayTimeColsView; + exports.DelayedRunner = DelayedRunner; + exports.Draggable = ExternalDraggable; + exports.ElementDragging = ElementDragging; + exports.ElementScrollController = ElementScrollController; + exports.Emitter = Emitter; + exports.EventApi = EventApi; + exports.EventRoot = EventRoot; + exports.EventSourceApi = EventSourceApi; + exports.FeaturefulElementDragging = FeaturefulElementDragging; + exports.Fragment = Fragment; + exports.Interaction = Interaction; + exports.ListView = ListView; + exports.MountHook = MountHook; + exports.NamedTimeZoneImpl = NamedTimeZoneImpl; + exports.NowIndicatorRoot = NowIndicatorRoot; + exports.NowTimer = NowTimer; + exports.PointerDragging = PointerDragging; + exports.PositionCache = PositionCache; + exports.RefMap = RefMap; + exports.RenderHook = RenderHook; + exports.ScrollController = ScrollController; + exports.ScrollResponder = ScrollResponder; + exports.Scroller = Scroller; + exports.SimpleScrollGrid = SimpleScrollGrid; + exports.Slicer = Slicer; + exports.Splitter = Splitter; + exports.StandardEvent = StandardEvent; + exports.Table = Table; + exports.TableDateCell = TableDateCell; + exports.TableDowCell = TableDowCell; + exports.TableView = TableView; + exports.Theme = Theme; + exports.ThirdPartyDraggable = ThirdPartyDraggable; + exports.TimeCols = TimeCols; + exports.TimeColsSlatsCoords = TimeColsSlatsCoords; + exports.TimeColsView = TimeColsView; + exports.ViewApi = ViewApi; + exports.ViewContextType = ViewContextType; + exports.ViewRoot = ViewRoot; + exports.WeekNumberRoot = WeekNumberRoot; + exports.WindowScrollController = WindowScrollController; + exports.addDays = addDays; + exports.addDurations = addDurations; + exports.addMs = addMs; + exports.addWeeks = addWeeks; + exports.allowContextMenu = allowContextMenu; + exports.allowSelection = allowSelection; + exports.applyMutationToEventStore = applyMutationToEventStore; + exports.applyStyle = applyStyle; + exports.applyStyleProp = applyStyleProp; + exports.asCleanDays = asCleanDays; + exports.asRoughMinutes = asRoughMinutes; + exports.asRoughMs = asRoughMs; + exports.asRoughSeconds = asRoughSeconds; + exports.buildClassNameNormalizer = buildClassNameNormalizer; + exports.buildDayRanges = buildDayRanges; + exports.buildDayTableModel = buildDayTableModel; + exports.buildEventApis = buildEventApis; + exports.buildEventRangeKey = buildEventRangeKey; + exports.buildHashFromArray = buildHashFromArray; + exports.buildNavLinkData = buildNavLinkData; + exports.buildSegCompareObj = buildSegCompareObj; + exports.buildSegTimeText = buildSegTimeText; + exports.buildSlatMetas = buildSlatMetas; + exports.buildTimeColsModel = buildTimeColsModel; + exports.collectFromHash = collectFromHash; + exports.combineEventUis = combineEventUis; + exports.compareByFieldSpec = compareByFieldSpec; + exports.compareByFieldSpecs = compareByFieldSpecs; + exports.compareNumbers = compareNumbers; + exports.compareObjs = compareObjs; + exports.computeEdges = computeEdges; + exports.computeFallbackHeaderFormat = computeFallbackHeaderFormat; + exports.computeHeightAndMargins = computeHeightAndMargins; + exports.computeInnerRect = computeInnerRect; + exports.computeRect = computeRect; + exports.computeSegDraggable = computeSegDraggable; + exports.computeSegEndResizable = computeSegEndResizable; + exports.computeSegStartResizable = computeSegStartResizable; + exports.computeShrinkWidth = computeShrinkWidth; + exports.computeSmallestCellWidth = computeSmallestCellWidth; + exports.computeVisibleDayRange = computeVisibleDayRange; + exports.config = config; + exports.constrainPoint = constrainPoint; + exports.createContext = createContext$1; + exports.createDuration = createDuration; + exports.createElement = createElement; + exports.createEmptyEventStore = createEmptyEventStore; + exports.createEventInstance = createEventInstance; + exports.createEventUi = createEventUi; + exports.createFormatter = createFormatter; + exports.createPlugin = createPlugin; + exports.createRef = createRef; + exports.diffDates = diffDates; + exports.diffDayAndTime = diffDayAndTime; + exports.diffDays = diffDays; + exports.diffPoints = diffPoints; + exports.diffWeeks = diffWeeks; + exports.diffWholeDays = diffWholeDays; + exports.diffWholeWeeks = diffWholeWeeks; + exports.disableCursor = disableCursor; + exports.elementClosest = elementClosest; + exports.elementMatches = elementMatches; + exports.enableCursor = enableCursor; + exports.eventTupleToStore = eventTupleToStore; + exports.filterEventStoreDefs = filterEventStoreDefs; + exports.filterHash = filterHash; + exports.findDirectChildren = findDirectChildren; + exports.findElements = findElements; + exports.flexibleCompare = flexibleCompare; + exports.flushToDom = flushToDom$1; + exports.formatDate = formatDate; + exports.formatDayString = formatDayString; + exports.formatIsoTimeString = formatIsoTimeString; + exports.formatRange = formatRange; + exports.getAllowYScrolling = getAllowYScrolling; + exports.getCanVGrowWithinCell = getCanVGrowWithinCell; + exports.getClippingParents = getClippingParents; + exports.getDateMeta = getDateMeta; + exports.getDayClassNames = getDayClassNames; + exports.getDefaultEventEnd = getDefaultEventEnd; + exports.getElSeg = getElSeg; + exports.getEventClassNames = getEventClassNames; + exports.getIsRtlScrollbarOnLeft = getIsRtlScrollbarOnLeft; + exports.getRectCenter = getRectCenter; + exports.getRelevantEvents = getRelevantEvents; + exports.getScrollGridClassNames = getScrollGridClassNames; + exports.getScrollbarWidths = getScrollbarWidths; + exports.getSectionClassNames = getSectionClassNames; + exports.getSectionHasLiquidHeight = getSectionHasLiquidHeight; + exports.getSegMeta = getSegMeta; + exports.getSlotClassNames = getSlotClassNames; + exports.getStickyFooterScrollbar = getStickyFooterScrollbar; + exports.getStickyHeaderDates = getStickyHeaderDates; + exports.getUnequalProps = getUnequalProps; + exports.globalLocales = globalLocales; + exports.globalPlugins = globalPlugins; + exports.greatestDurationDenominator = greatestDurationDenominator; + exports.guid = guid; + exports.hasBgRendering = hasBgRendering; + exports.hasShrinkWidth = hasShrinkWidth; + exports.identity = identity; + exports.interactionSettingsStore = interactionSettingsStore; + exports.interactionSettingsToStore = interactionSettingsToStore; + exports.intersectRanges = intersectRanges; + exports.intersectRects = intersectRects; + exports.isArraysEqual = isArraysEqual; + exports.isColPropsEqual = isColPropsEqual; + exports.isDateSpansEqual = isDateSpansEqual; + exports.isInt = isInt; + exports.isInteractionValid = isInteractionValid; + exports.isMultiDayRange = isMultiDayRange; + exports.isPropsEqual = isPropsEqual; + exports.isPropsValid = isPropsValid; + exports.isValidDate = isValidDate; + exports.listenBySelector = listenBySelector; + exports.mapHash = mapHash; + exports.memoize = memoize; + exports.memoizeArraylike = memoizeArraylike; + exports.memoizeHashlike = memoizeHashlike; + exports.memoizeObjArg = memoizeObjArg; + exports.mergeEventStores = mergeEventStores; + exports.multiplyDuration = multiplyDuration; + exports.padStart = padStart; + exports.parseBusinessHours = parseBusinessHours; + exports.parseClassNames = parseClassNames; + exports.parseDragMeta = parseDragMeta; + exports.parseEventDef = parseEventDef; + exports.parseFieldSpecs = parseFieldSpecs; + exports.parseMarker = parse; + exports.pointInsideRect = pointInsideRect; + exports.preventContextMenu = preventContextMenu; + exports.preventDefault = preventDefault; + exports.preventSelection = preventSelection; + exports.rangeContainsMarker = rangeContainsMarker; + exports.rangeContainsRange = rangeContainsRange; + exports.rangesEqual = rangesEqual; + exports.rangesIntersect = rangesIntersect; + exports.refineEventDef = refineEventDef; + exports.refineProps = refineProps; + exports.removeElement = removeElement; + exports.removeExact = removeExact; + exports.render = render; + exports.renderChunkContent = renderChunkContent; + exports.renderFill = renderFill; + exports.renderMicroColGroup = renderMicroColGroup; + exports.renderScrollShim = renderScrollShim; + exports.requestJson = requestJson; + exports.sanitizeShrinkWidth = sanitizeShrinkWidth; + exports.setElSeg = setElSeg; + exports.setRef = setRef; + exports.sliceEventStore = sliceEventStore; + exports.sliceEvents = sliceEvents; + exports.sortEventSegs = sortEventSegs; + exports.startOfDay = startOfDay; + exports.translateRect = translateRect; + exports.triggerDateSelect = triggerDateSelect; + exports.unpromisify = unpromisify; + exports.version = version; + exports.whenTransitionDone = whenTransitionDone; + exports.wholeDivideDurations = wholeDivideDurations; + + return exports; + +}({})); diff --git a/static/fullcalendar/main.min.css b/static/fullcalendar/main.min.css new file mode 100644 index 0000000..48a87a8 --- /dev/null +++ b/static/fullcalendar/main.min.css @@ -0,0 +1 @@ +.fc-icon,.fc-unselectable{-moz-user-select:none;-ms-user-select:none}.fc .fc-button,.fc-icon{text-transform:none;text-align:center}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc .fc-button:not(:disabled),.fc a[data-navlink],.fc-event.fc-event-draggable,.fc-event[href]{cursor:pointer}.fc-unselectable{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.fc{display:flex;flex-direction:column;font-size:1em}.fc .fc-button,.fc-icon{display:inline-block;font-weight:400}.fc,.fc *,.fc :after,.fc :before{box-sizing:border-box}.fc table{border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{vertical-align:top;padding:0}.fc .fc-button,.fc .fc-button .fc-icon,.fc .fc-button-group,.fc .fc-timegrid-slot-label{vertical-align:middle}.fc a[data-navlink]:hover{text-decoration:underline}.fc .fc-button:hover,.fc .fc-list-event-title a,a.fc-event,a.fc-event:hover{text-decoration:none}.fc-direction-ltr{direction:ltr;text-align:left}.fc-direction-rtl{direction:rtl;text-align:right}.fc-theme-standard td,.fc-theme-standard th{border:1px solid #ddd;border:1px solid var(--fc-border-color,#ddd)}.fc-liquid-hack td,.fc-liquid-hack th{position:relative}@font-face{font-family:fcicons;src:url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format('truetype');font-weight:400;font-style:normal}.fc-icon{width:1em;height:1em;-webkit-user-select:none;user-select:none;font-family:fcicons!important;speak:none;font-style:normal;font-variant:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fc-icon-chevron-left:before{content:"\e900"}.fc-icon-chevron-right:before{content:"\e901"}.fc-icon-chevrons-left:before{content:"\e902"}.fc-icon-chevrons-right:before{content:"\e903"}.fc-icon-minus-square:before{content:"\e904"}.fc-icon-plus-square:before{content:"\e905"}.fc-icon-x:before{content:"\e906"}.fc .fc-button{overflow:visible;text-transform:none;margin:0;font-family:inherit}.fc .fc-button::-moz-focus-inner{padding:0;border-style:none}.fc .fc-button{-webkit-appearance:button;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.4em .65em;font-size:1em;line-height:1.5;border-radius:.25em}.fc .fc-button:focus{outline:0;box-shadow:0 0 0 .2rem rgba(44,62,80,.25)}.fc .fc-button-primary:focus,.fc .fc-button-primary:not(:disabled).fc-button-active:focus,.fc .fc-button-primary:not(:disabled):active:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc .fc-button:disabled{opacity:.65}.fc .fc-button-primary{color:#fff;color:var(--fc-button-text-color,#fff);background-color:#2C3E50;background-color:var(--fc-button-bg-color,#2C3E50);border-color:#2C3E50;border-color:var(--fc-button-border-color,#2C3E50)}.fc .fc-button-primary:hover{color:#fff;color:var(--fc-button-text-color,#fff);background-color:#1e2b37;background-color:var(--fc-button-hover-bg-color,#1e2b37);border-color:#1a252f;border-color:var(--fc-button-hover-border-color,#1a252f)}.fc .fc-button-primary:disabled{color:#fff;color:var(--fc-button-text-color,#fff);background-color:#2C3E50;background-color:var(--fc-button-bg-color,#2C3E50);border-color:#2C3E50;border-color:var(--fc-button-border-color,#2C3E50)}.fc .fc-button-primary:not(:disabled).fc-button-active,.fc .fc-button-primary:not(:disabled):active{color:#fff;color:var(--fc-button-text-color,#fff);background-color:#1a252f;background-color:var(--fc-button-active-bg-color,#1a252f);border-color:#151e27;border-color:var(--fc-button-active-border-color,#151e27)}.fc .fc-button .fc-icon{font-size:1.5em}.fc .fc-button-group{position:relative;display:inline-flex}.fc .fc-button-group>.fc-button{position:relative;flex:1 1 auto}.fc .fc-button-group>.fc-button.fc-button-active,.fc .fc-button-group>.fc-button:active,.fc .fc-button-group>.fc-button:focus,.fc .fc-button-group>.fc-button:hover{z-index:1}.fc-direction-ltr .fc-button-group>.fc-button:not(:first-child){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-direction-ltr .fc-button-group>.fc-button:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.fc-direction-rtl .fc-button-group>.fc-button:not(:first-child){margin-right:-1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-direction-rtl .fc-button-group>.fc-button:not(:last-child){border-top-left-radius:0;border-bottom-left-radius:0}.fc .fc-toolbar{display:flex;justify-content:space-between;align-items:center}.fc .fc-toolbar.fc-header-toolbar{margin-bottom:1.5em}.fc .fc-toolbar.fc-footer-toolbar{margin-top:1.5em}.fc .fc-toolbar-title{font-size:1.75em;margin:0}.fc-direction-ltr .fc-toolbar>*>:not(:first-child){margin-left:.75em}.fc-direction-rtl .fc-toolbar>*>:not(:first-child){margin-right:.75em}.fc-direction-rtl .fc-toolbar-ltr{flex-direction:row-reverse}.fc .fc-scroller{-webkit-overflow-scrolling:touch;position:relative}.fc .fc-scroller-liquid{height:100%}.fc .fc-scroller-liquid-absolute{position:absolute;top:0;right:0;left:0;bottom:0}.fc .fc-scroller-harness{position:relative;overflow:hidden;direction:ltr}.fc .fc-scroller-harness-liquid{height:100%}.fc-direction-rtl .fc-scroller-harness>.fc-scroller{direction:rtl}.fc-theme-standard .fc-scrollgrid{border:1px solid #ddd;border:1px solid var(--fc-border-color,#ddd)}.fc .fc-scrollgrid,.fc .fc-scrollgrid-section-footer>*,.fc .fc-scrollgrid-section-header>*{border-bottom-width:0}.fc .fc-scrollgrid,.fc .fc-scrollgrid table{width:100%;table-layout:fixed}.fc .fc-scrollgrid table{border-top-style:hidden;border-left-style:hidden;border-right-style:hidden}.fc .fc-scrollgrid{border-collapse:separate;border-right-width:0}.fc .fc-scrollgrid-liquid{height:100%}.fc .fc-scrollgrid-section,.fc .fc-scrollgrid-section table,.fc .fc-scrollgrid-section>td{height:1px}.fc .fc-scrollgrid-section-liquid{height:auto}.fc .fc-scrollgrid-section-liquid>td{height:100%}.fc .fc-scrollgrid-section>*{border-top-width:0;border-left-width:0}.fc .fc-scrollgrid-section-body table,.fc .fc-scrollgrid-section-footer table{border-bottom-style:hidden}.fc .fc-scrollgrid-section-sticky>*{background:var(--fc-page-bg-color,#fff);position:-webkit-sticky;position:sticky;z-index:2}.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky>*{top:0}.fc .fc-scrollgrid-section-footer.fc-scrollgrid-section-sticky>*{bottom:0}.fc .fc-scrollgrid-sticky-shim{height:1px;margin-bottom:-1px}.fc-sticky{position:-webkit-sticky;position:sticky}.fc .fc-view-harness{flex-grow:1;position:relative}.fc .fc-bg-event,.fc .fc-highlight,.fc .fc-non-business,.fc .fc-view-harness-active>.fc-view{position:absolute;top:0;left:0;right:0;bottom:0}.fc .fc-col-header-cell-cushion{display:inline-block;padding:2px 4px}.fc .fc-non-business{background:rgba(215,215,215,.3);background:var(--fc-non-business-color,rgba(215,215,215,.3))}.fc .fc-bg-event{background:var(--fc-bg-event-color,#8fdf82);opacity:.3;opacity:var(--fc-bg-event-opacity,.3)}.fc .fc-bg-event .fc-event-title{margin:.5em;font-size:.85em;font-size:var(--fc-small-font-size,.85em);font-style:italic}.fc .fc-highlight{background:rgba(188,232,241,.3);background:var(--fc-highlight-color,rgba(188,232,241,.3))}.fc .fc-cell-shaded,.fc .fc-day-disabled{background:rgba(208,208,208,.3);background:var(--fc-neutral-bg-color,rgba(208,208,208,.3))}.fc-event .fc-event-main{position:relative;z-index:2}.fc-event-dragging:not(.fc-event-selected){opacity:.75}.fc-event-dragging.fc-event-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event .fc-event-resizer{display:none;position:absolute;z-index:4}.fc-event-selected .fc-event-resizer,.fc-event:hover .fc-event-resizer,.fc-h-event{display:block}.fc-event-selected .fc-event-resizer{border-radius:4px;border-radius:calc(var(--fc-event-resizer-dot-total-width,8px)/ 2);border-width:1px;border-width:var(--fc-event-resizer-dot-border-width,1px);width:8px;width:var(--fc-event-resizer-dot-total-width,8px);height:8px;height:var(--fc-event-resizer-dot-total-width,8px);border-style:solid;border-color:inherit;background:var(--fc-page-bg-color,#fff)}.fc-event-selected .fc-event-resizer:before{content:'';position:absolute;top:-20px;left:-20px;right:-20px;bottom:-20px}.fc-event-selected{box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event-selected:before{content:"";position:absolute;z-index:3;top:0;left:0;right:0;bottom:0}.fc-event-selected:after{content:"";background:rgba(0,0,0,.25);background:var(--fc-event-selected-overlay-color,rgba(0,0,0,.25));position:absolute;z-index:1;top:-1px;left:-1px;right:-1px;bottom:-1px}.fc-h-event{border:1px solid #3788d8;border:1px solid var(--fc-event-border-color,#3788d8);background-color:#3788d8;background-color:var(--fc-event-bg-color,#3788d8)}.fc-h-event .fc-event-main{color:#fff;color:var(--fc-event-text-color,#fff)}.fc-h-event .fc-event-main-frame{display:flex}.fc-h-event .fc-event-time{max-width:100%;overflow:hidden}.fc-h-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-width:0}.fc-h-event .fc-event-title{display:inline-block;vertical-align:top;left:0;right:0;max-width:100%;overflow:hidden}.fc-h-event.fc-event-selected:before{top:-10px;bottom:-10px}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end){border-top-left-radius:0;border-bottom-left-radius:0;border-left-width:0}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start){border-top-right-radius:0;border-bottom-right-radius:0;border-right-width:0}.fc-h-event:not(.fc-event-selected) .fc-event-resizer{top:0;bottom:0;width:8px;width:var(--fc-event-resizer-thickness,8px)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end{cursor:w-resize;left:-4px;left:calc(var(--fc-event-resizer-thickness,8px)/ -2)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start{cursor:e-resize;right:-4px;right:calc(var(--fc-event-resizer-thickness,8px)/ -2)}.fc-h-event.fc-event-selected .fc-event-resizer{top:50%;margin-top:-4px;margin-top:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end{left:-4px;left:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start{right:-4px;right:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}:root{--fc-daygrid-event-dot-width:8px;--fc-list-event-dot-width:10px;--fc-list-event-hover-bg-color:#f5f5f5}.fc .fc-popover{position:fixed;top:0;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc .fc-popover-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding:3px 4px}.fc .fc-popover-title{margin:0 2px}.fc .fc-popover-close{cursor:pointer;opacity:.65;font-size:1.1em}.fc-theme-standard .fc-popover{border:1px solid #ddd;border:1px solid var(--fc-border-color,#ddd);background:var(--fc-page-bg-color,#fff)}.fc-theme-standard .fc-popover-header{background:rgba(208,208,208,.3);background:var(--fc-neutral-bg-color,rgba(208,208,208,.3))}.fc-daygrid-day-events:after,.fc-daygrid-day-events:before,.fc-daygrid-day-frame:after,.fc-daygrid-day-frame:before,.fc-daygrid-event-harness:after,.fc-daygrid-event-harness:before{content:"";clear:both;display:table}.fc .fc-daygrid-body{position:relative;z-index:1}.fc .fc-daygrid-day.fc-day-today{background-color:rgba(255,220,40,.15);background-color:var(--fc-today-bg-color,rgba(255,220,40,.15))}.fc .fc-daygrid-day-frame{position:relative;min-height:100%}.fc .fc-daygrid-day-top{display:flex;flex-direction:row-reverse}.fc .fc-day-other .fc-daygrid-day-top{opacity:.3}.fc .fc-daygrid-day-number{position:relative;z-index:4;padding:4px}.fc .fc-daygrid-day-events{margin-top:1px}.fc .fc-daygrid-body-balanced .fc-daygrid-day-events{position:absolute;left:0;right:0}.fc .fc-daygrid-body-unbalanced .fc-daygrid-day-events{position:relative;min-height:2em}.fc .fc-daygrid-body-natural .fc-daygrid-day-events{margin-bottom:1em}.fc .fc-daygrid-event-harness{position:relative}.fc .fc-daygrid-event-harness-abs{position:absolute;top:0;left:0;right:0}.fc .fc-daygrid-bg-harness{position:absolute;top:0;bottom:0}.fc .fc-daygrid-day-bg .fc-non-business{z-index:1}.fc .fc-daygrid-day-bg .fc-bg-event{z-index:2}.fc .fc-daygrid-day-bg .fc-highlight{z-index:3}.fc .fc-daygrid-event{z-index:6;margin-top:1px}.fc .fc-daygrid-event.fc-event-mirror{z-index:7}.fc .fc-daygrid-day-bottom{font-size:.85em;margin:2px 3px 0}.fc .fc-daygrid-more-link{position:relative;z-index:4;cursor:pointer}.fc .fc-daygrid-week-number{position:absolute;z-index:5;top:0;padding:2px;min-width:1.5em;text-align:center;background-color:rgba(208,208,208,.3);background-color:var(--fc-neutral-bg-color,rgba(208,208,208,.3));color:grey;color:var(--fc-neutral-text-color,grey)}.fc .fc-more-popover{z-index:8}.fc .fc-more-popover .fc-popover-body{min-width:220px;padding:10px}.fc-direction-ltr .fc-daygrid-event.fc-event-start,.fc-direction-rtl .fc-daygrid-event.fc-event-end{margin-left:2px}.fc-direction-ltr .fc-daygrid-event.fc-event-end,.fc-direction-rtl .fc-daygrid-event.fc-event-start{margin-right:2px}.fc-direction-ltr .fc-daygrid-week-number{left:0;border-radius:0 0 3px}.fc-direction-rtl .fc-daygrid-week-number{right:0;border-radius:0 0 0 3px}.fc-liquid-hack .fc-daygrid-day-frame{position:static}.fc-daygrid-event{position:relative;white-space:nowrap;border-radius:3px;font-size:.85em;font-size:var(--fc-small-font-size,.85em)}.fc-daygrid-block-event .fc-event-time{font-weight:700}.fc-daygrid-block-event .fc-event-time,.fc-daygrid-block-event .fc-event-title{padding:1px}.fc-daygrid-dot-event{display:flex;align-items:center;padding:2px 0}.fc-daygrid-dot-event .fc-event-title{flex-grow:1;flex-shrink:1;min-width:0;overflow:hidden;font-weight:700}.fc-daygrid-dot-event.fc-event-mirror,.fc-daygrid-dot-event:hover{background:rgba(0,0,0,.1)}.fc-daygrid-dot-event.fc-event-selected:before{top:-10px;bottom:-10px}.fc-daygrid-event-dot{margin:0 4px;box-sizing:content-box;width:0;height:0;border:4px solid #3788d8;border:calc(var(--fc-daygrid-event-dot-width,8px)/ 2) solid var(--fc-event-border-color,#3788d8);border-radius:4px;border-radius:calc(var(--fc-daygrid-event-dot-width,8px)/ 2)}.fc-direction-ltr .fc-daygrid-event .fc-event-time{margin-right:3px}.fc-direction-rtl .fc-daygrid-event .fc-event-time{margin-left:3px}.fc-v-event{display:block;border:1px solid #3788d8;border:1px solid var(--fc-event-border-color,#3788d8);background-color:#3788d8;background-color:var(--fc-event-bg-color,#3788d8)}.fc-v-event .fc-event-main{color:#fff;color:var(--fc-event-text-color,#fff);height:100%}.fc-v-event .fc-event-main-frame{height:100%;display:flex;flex-direction:column}.fc-v-event .fc-event-time{flex-grow:0;flex-shrink:0;max-height:100%;overflow:hidden}.fc-v-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-height:0}.fc-v-event .fc-event-title{top:0;bottom:0;max-height:100%;overflow:hidden}.fc-v-event:not(.fc-event-start){border-top-width:0;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event:not(.fc-event-end){border-bottom-width:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-v-event.fc-event-selected:before{left:-10px;right:-10px}.fc-v-event .fc-event-resizer-start{cursor:n-resize}.fc-v-event .fc-event-resizer-end{cursor:s-resize}.fc-v-event:not(.fc-event-selected) .fc-event-resizer{height:8px;height:var(--fc-event-resizer-thickness,8px);left:0;right:0}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-start{top:-4px;top:calc(var(--fc-event-resizer-thickness,8px)/ -2)}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-end{bottom:-4px;bottom:calc(var(--fc-event-resizer-thickness,8px)/ -2)}.fc-v-event.fc-event-selected .fc-event-resizer{left:50%;margin-left:-4px;margin-left:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}.fc-v-event.fc-event-selected .fc-event-resizer-start{top:-4px;top:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}.fc-v-event.fc-event-selected .fc-event-resizer-end{bottom:-4px;bottom:calc(var(--fc-event-resizer-dot-total-width,8px)/ -2)}.fc .fc-timegrid .fc-daygrid-body{z-index:2}.fc .fc-timegrid-axis-chunk>table,.fc .fc-timegrid-body,.fc .fc-timegrid-slots{position:relative;z-index:1}.fc .fc-timegrid-divider{padding:0 0 2px}.fc .fc-timegrid-body{min-height:100%}.fc .fc-timegrid-axis-chunk{position:relative}.fc .fc-timegrid-slot{height:1.5em;border-bottom:0}.fc .fc-timegrid-slot:empty:before{content:'\00a0'}.fc .fc-timegrid-slot-minor{border-top-style:dotted}.fc .fc-timegrid-slot-label-cushion{display:inline-block;white-space:nowrap}.fc .fc-timegrid-axis-cushion,.fc .fc-timegrid-slot-label-cushion{padding:0 4px}.fc .fc-timegrid-axis-frame-liquid{height:100%}.fc .fc-timegrid-axis-frame{overflow:hidden;display:flex;align-items:center;justify-content:flex-end}.fc .fc-timegrid-axis-cushion{max-width:60px;flex-shrink:0}.fc-direction-ltr .fc-timegrid-slot-label-frame{text-align:right}.fc-direction-rtl .fc-timegrid-slot-label-frame{text-align:left}.fc-liquid-hack .fc-timegrid-axis-frame-liquid{height:auto;position:absolute;top:0;right:0;bottom:0;left:0}.fc .fc-timegrid-col.fc-day-today{background-color:rgba(255,220,40,.15);background-color:var(--fc-today-bg-color,rgba(255,220,40,.15))}.fc .fc-timegrid-col-frame{min-height:100%;position:relative}.fc-liquid-hack .fc-timegrid-col-frame{height:auto;position:absolute;top:0;right:0;bottom:0;left:0}.fc-media-screen .fc-timegrid-cols{position:absolute;top:0;left:0;right:0;bottom:0}.fc-media-screen .fc-timegrid-cols>table{height:100%}.fc-media-screen .fc-timegrid-col-bg,.fc-media-screen .fc-timegrid-col-events,.fc-media-screen .fc-timegrid-now-indicator-container{position:absolute;top:0;left:0;right:0}.fc-media-screen .fc-timegrid-event-harness{position:absolute}.fc .fc-timegrid-col-bg{z-index:2}.fc .fc-timegrid-col-bg .fc-non-business{z-index:1}.fc .fc-timegrid-col-bg .fc-bg-event{z-index:2}.fc .fc-timegrid-col-bg .fc-highlight,.fc .fc-timegrid-col-events{z-index:3}.fc .fc-timegrid-bg-harness{position:absolute;left:0;right:0}.fc .fc-timegrid-now-indicator-container{bottom:0;overflow:hidden}.fc-direction-ltr .fc-timegrid-col-events{margin:0 2.5% 0 2px}.fc-direction-rtl .fc-timegrid-col-events{margin:0 2px 0 2.5%}.fc-timegrid-event-harness-inset .fc-timegrid-event,.fc-timegrid-event.fc-event-mirror{box-shadow:0 0 0 1px #fff;box-shadow:0 0 0 1px var(--fc-page-bg-color,#fff)}.fc-timegrid-event{font-size:.85em;font-size:var(--fc-small-font-size,.85em);border-radius:3px}.fc-timegrid-event .fc-event-main{padding:1px 1px 0}.fc-timegrid-event .fc-event-time{white-space:nowrap;font-size:.85em;font-size:var(--fc-small-font-size,.85em);margin-bottom:1px}.fc-timegrid-event-condensed .fc-event-main-frame{flex-direction:row;overflow:hidden}.fc-timegrid-event-condensed .fc-event-time:after{content:'\00a0-\00a0'}.fc-timegrid-event-condensed .fc-event-title{font-size:.85em;font-size:var(--fc-small-font-size,.85em)}.fc-media-screen .fc-timegrid-event{position:absolute;top:0;bottom:1px;left:0;right:0}.fc .fc-timegrid-now-indicator-line{position:absolute;z-index:4;left:0;right:0;border-style:solid;border-color:red;border-color:var(--fc-now-indicator-color,red);border-width:1px 0 0}.fc .fc-timegrid-now-indicator-arrow{position:absolute;z-index:4;margin-top:-5px;border-style:solid;border-color:red;border-color:var(--fc-now-indicator-color,red)}.fc-direction-ltr .fc-timegrid-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-direction-rtl .fc-timegrid-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-theme-standard .fc-list{border:1px solid #ddd;border:1px solid var(--fc-border-color,#ddd)}.fc .fc-list-empty{background-color:rgba(208,208,208,.3);background-color:var(--fc-neutral-bg-color,rgba(208,208,208,.3));height:100%;display:flex;justify-content:center;align-items:center}.fc .fc-list-empty-cushion{margin:5em 0}.fc .fc-list-table{width:100%;border-style:hidden}.fc .fc-list-table tr>*{border-left:0;border-right:0}.fc .fc-list-sticky .fc-list-day>*{position:-webkit-sticky;position:sticky;top:0;background:var(--fc-page-bg-color,#fff)}.fc .fc-list-table th{padding:0}.fc .fc-list-day-cushion,.fc .fc-list-table td{padding:8px 14px}.fc .fc-list-day-cushion:after{content:"";clear:both;display:table}.fc-theme-standard .fc-list-day-cushion{background-color:rgba(208,208,208,.3);background-color:var(--fc-neutral-bg-color,rgba(208,208,208,.3))}.fc-direction-ltr .fc-list-day-text,.fc-direction-rtl .fc-list-day-side-text{float:left}.fc-direction-ltr .fc-list-day-side-text,.fc-direction-rtl .fc-list-day-text{float:right}.fc-direction-ltr .fc-list-table .fc-list-event-graphic{padding-right:0}.fc-direction-rtl .fc-list-table .fc-list-event-graphic{padding-left:0}.fc .fc-list-event.fc-event-forced-url{cursor:pointer}.fc .fc-list-event:hover td{background-color:#f5f5f5;background-color:var(--fc-list-event-hover-bg-color,#f5f5f5)}.fc .fc-list-event-graphic,.fc .fc-list-event-time{white-space:nowrap;width:1px}.fc .fc-list-event-dot{display:inline-block;box-sizing:content-box;width:0;height:0;border:5px solid #3788d8;border:calc(var(--fc-list-event-dot-width,10px)/ 2) solid var(--fc-event-border-color,#3788d8);border-radius:5px;border-radius:calc(var(--fc-list-event-dot-width,10px)/ 2)}.fc .fc-list-event-title a{color:inherit}.fc .fc-list-event.fc-event-forced-url:hover a{text-decoration:underline}.fc-theme-bootstrap a:not([href]){color:inherit} \ No newline at end of file diff --git a/static/fullcalendar/main.min.js b/static/fullcalendar/main.min.js new file mode 100644 index 0000000..055eb02 --- /dev/null +++ b/static/fullcalendar/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar v5.3.2 +Docs & License: https://fullcalendar.io/ +(c) 2020 Adam Shaw +*/ +var FullCalendar=function(e){"use strict";var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,n)};function n(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}var r=function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n3)for(n=[n],r=3;r=0;i--){var a=e[i][r];if("object"==typeof a&&a)o.unshift(a);else if(void 0!==a){n[r]=a;break}}o.length&&(n[r]=Ne(o))}for(i=e.length-1;i>=0;i--){var s=e[i];for(var l in s)l in n||(n[l]=s[l])}return n}function He(e,t){var n={};for(var r in e)t(e[r],r)&&(n[r]=e[r]);return n}function Oe(e,t){var n={};for(var r in e)n[r]=t(e[r],r);return n}function Ae(e){for(var t={},n=0,r=e;n1)||"numeric"!==o.year&&"2-digit"!==o.year||"numeric"!==o.month&&"2-digit"!==o.month||"numeric"!==o.day&&"2-digit"!==o.day||(s=1);var l=this.format(e,n),u=this.format(t,n);if(l===u)return l;var c=vt(function(e,t){var n={};for(var r in e)(!(r in lt)||lt[r]<=t)&&(n[r]=e[r]);return n}(o,s),i,n),d=c(e),p=c(t),f=function(e,t,n,r){var o=0;for(;o=$e(t)&&(r=ve(r,1))}return e.start&&(n=be(e.start),r&&r<=n&&(r=ve(n,1))),{start:n,end:r}}function $t(e){var t=Jt(e);return ye(t.start,t.end)>1}function Qt(e,t,n,r){return"year"===r?Ye(n.diffWholeYears(e,t),"year"):"month"===r?Ye(n.diffWholeMonths(e,t),"month"):Ee(e,t)}function en(e,t){var n,r,o=[],i=t.start;for(e.sort(tn),n=0;ni&&o.push({start:i,end:r.start}),r.end>i&&(i=r.end);return it.start)&&(null===e.start||null===t.end||e.start=e.start)&&(null===e.end||null!==t.end&&t.end<=e.end)}function sn(e,t){return(null===e.start||t>=e.start)&&(null===e.end||t=(n||t.end),isToday:t&&sn(t,r.start)}}function Dn(e){var t=["fc-event"];return e.isMirror&&t.push("fc-event-mirror"),e.isDraggable&&t.push("fc-event-draggable"),(e.isStartResizable||e.isEndResizable)&&t.push("fc-event-resizable"),e.isDragging&&t.push("fc-event-dragging"),e.isResizing&&t.push("fc-event-resizing"),e.isSelected&&t.push("fc-event-selected"),e.isStart&&t.push("fc-event-start"),e.isEnd&&t.push("fc-event-end"),e.isPast&&t.push("fc-event-past"),e.isToday&&t.push("fc-event-today"),e.isFuture&&t.push("fc-event-future"),t}function bn(e){return e.instance?e.instance.instanceId:e.def.defId+":"+e.range.start.toISOString()}var Cn={start:_t,end:_t,allDay:Boolean};function wn(e,t,n){var o=function(e,t){var n=xt(e,Cn),o=n.refined,i=n.extra,a=o.start?t.createMarkerMeta(o.start):null,s=o.end?t.createMarkerMeta(o.end):null,l=o.allDay;null==l&&(l=a&&a.isTimeUnspecified&&(!s||s.isTimeUnspecified));return r({range:{start:a?a.marker:null,end:s?s.marker:null},allDay:l},i)}(e,t),i=o.range;if(!i.start)return null;if(!i.end){if(null==n)return null;i.end=t.add(i.start,n)}return o}function Rn(e,t){return rn(e.range,t.range)&&e.allDay===t.allDay&&function(e,t){for(var n in t)if("range"!==n&&"allDay"!==n&&e[n]!==t[n])return!1;for(var n in e)if(!(n in t))return!1;return!0}(e,t)}function Tn(e,t,n){return r(r({},kn(e,t,n)),{timeZone:t.timeZone})}function kn(e,t,n){return{start:t.toDate(e.start),end:t.toDate(e.end),startStr:t.formatIso(e.start,{omitTime:n}),endStr:t.formatIso(e.end,{omitTime:n})}}function Mn(e,t,n){var r=Yt({editable:!1},n),o=Xt(r.refined,r.extra,"",e.allDay,!0,n);return{def:o,ui:fn(o,t),instance:Ie(o.defId,e.range),range:e.range,isStart:!0,isEnd:!0}}function xn(e,t,n){n.emitter.trigger("select",r(r({},_n(e,n)),{jsEvent:t?t.origEvent:null,view:n.viewApi||n.calendarApi.view}))}function _n(e,t){for(var n,o,i={},a=0,s=t.pluginHooks.dateSpanTransforms;a=0;r--){var o=n[r].parseMeta(e);if(o)return{sourceDefId:r,meta:o}}return null}(i,t);if(s)return{_raw:e,isFetching:!1,latestFetchId:"",fetchRange:null,defaultAllDay:i.defaultAllDay,eventDataTransform:i.eventDataTransform,success:i.success,failure:i.failure,publicId:i.id||"",sourceId:$(),sourceDefId:s.sourceDefId,meta:s.meta,ui:Wt(i,t),extendedProps:a}}return null}function Ln(e){return r(r(r({},Lt),An),e.pluginHooks.eventSourceRefiners)}function Wn(e,t){return"function"==typeof e&&(e=e()),null==e?t.createNowMarker():t.createMarker(e)}var Vn=function(){function e(){}return e.prototype.getCurrentData=function(){return this.currentDataManager.getCurrentData()},e.prototype.dispatch=function(e){return this.currentDataManager.dispatch(e)},Object.defineProperty(e.prototype,"view",{get:function(){return this.getCurrentData().viewApi},enumerable:!1,configurable:!0}),e.prototype.batchRendering=function(e){e()},e.prototype.updateSize=function(){this.trigger("_resize",!0)},e.prototype.setOption=function(e,t){this.dispatch({type:"SET_OPTION",optionName:e,rawOptionValue:t})},e.prototype.getOption=function(e){return this.currentDataManager.currentCalendarOptionsInput[e]},e.prototype.getAvailableLocaleCodes=function(){return Object.keys(this.getCurrentData().availableRawLocales)},e.prototype.on=function(e,t){var n=this.currentDataManager;n.currentCalendarOptionsRefiners[e]?n.emitter.on(e,t):console.warn("Unknown listener name '"+e+"'")},e.prototype.off=function(e,t){this.currentDataManager.emitter.off(e,t)},e.prototype.trigger=function(e){for(var t,n=[],r=1;r=1?Math.min(o,i):o}(e,this.weekDow,this.weekDoy)},e.prototype.format=function(e,t,n){return void 0===n&&(n={}),t.format({marker:e,timeZoneOffset:null!=n.forcedTzo?n.forcedTzo:this.offsetForMarker(e)},this)},e.prototype.formatRange=function(e,t,n,r){return void 0===r&&(r={}),r.isEndExclusive&&(t=ge(t,-1)),n.formatRange({marker:e,timeZoneOffset:null!=r.forcedStartTzo?r.forcedStartTzo:this.offsetForMarker(e)},{marker:t,timeZoneOffset:null!=r.forcedEndTzo?r.forcedEndTzo:this.offsetForMarker(t)},this,r.defaultSeparator)},e.prototype.formatIso=function(e,t){void 0===t&&(t={});var n=null;return t.omitTimeZoneOffset||(n=null!=t.forcedTzo?t.forcedTzo:this.offsetForMarker(e)),function(e,t,n){void 0===n&&(n=!1);var r=e.toISOString();return r=r.replace(".000",""),n&&(r=r.replace("T00:00:00Z","")),r.length>10&&(null==t?r=r.replace("Z",""):0!==t&&(r=r.replace("Z",rt(t,!0)))),r}(e,n,t.omitTime)},e.prototype.timestampToMarker=function(e){return"local"===this.timeZone?Me(Re(new Date(e))):"UTC"!==this.timeZone&&this.namedTimeZoneImpl?Me(this.namedTimeZoneImpl.timestampToArray(e)):new Date(e)},e.prototype.offsetForMarker=function(e){return"local"===this.timeZone?-Te(ke(e)).getTimezoneOffset():"UTC"===this.timeZone?0:this.namedTimeZoneImpl?this.namedTimeZoneImpl.offsetForArray(ke(e)):null},e.prototype.toDate=function(e,t){return"local"===this.timeZone?Te(ke(e)):"UTC"===this.timeZone?new Date(e.valueOf()):this.namedTimeZoneImpl?new Date(e.valueOf()-1e3*this.namedTimeZoneImpl.offsetForArray(ke(e))*60):new Date(e.valueOf()-(t||0))},e}(),Kn=[],Jn={code:"en",week:{dow:0,doy:4},direction:"ltr",buttonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",year:"year",today:"today",month:"month",week:"week",day:"day",list:"list"},weekText:"W",allDayText:"all-day",moreLinkText:"more",noEventsText:"No events to display"};function $n(e){for(var t=e.length>0?e[0].code:"en",n=Kn.concat(e),r={en:Jn},o=0,i=n;o0;o--){var i=r.slice(0,o).join("-");if(t[i])return t[i]}return null}(n,t)||Jn;return er(e,n,r)}(e,t):er(e.code,[e.code],e)}function er(e,t,n){var r=Ne([Jn,n],["buttonText"]);delete r.code;var o=r.week;return delete r.week,{codeArg:e,codes:t,week:o,simpleNumberFormat:new Intl.NumberFormat(e),options:r}}function tr(e){var t=Qn(e.locale||"en",$n([]).map);return new Xn(r(r({timeZone:bt.timeZone,calendarSystem:"gregory"},e),{locale:t}))}var nr,rr={startTime:"09:00",endTime:"17:00",daysOfWeek:[1,2,3,4,5],display:"inverse-background",classNames:"fc-non-business",groupId:"_businessHours"};function or(e,t){return It(function(e){var t;t=!0===e?[{}]:Array.isArray(e)?e.filter((function(e){return e.daysOfWeek})):"object"==typeof e&&e?[e]:[];return t=t.map((function(e){return r(r({},rr),e)}))}(e),null,t)}function ir(e,t){return e.left>=t.left&&e.left=t.top&&e.top
/ elements with colspans. + SOLUTION: making individual
+ _this.frameElRefs = new RefMap(); // the fc-daygrid-day-frame + _this.fgElRefs = new RefMap(); // the fc-daygrid-day-events + _this.segHarnessRefs = new RefMap(); // indexed by "instanceId:firstCol" + _this.rootElRef = createRef(); + _this.state = { + framePositions: null, + maxContentHeight: null, + segHeights: {} + }; + return _this; + } + TableRow.prototype.render = function () { + var _this = this; + var _a = this, props = _a.props, state = _a.state, context = _a.context; + var colCnt = props.cells.length; + var businessHoursByCol = splitSegsByFirstCol(props.businessHourSegs, colCnt); + var bgEventSegsByCol = splitSegsByFirstCol(props.bgEventSegs, colCnt); + var highlightSegsByCol = splitSegsByFirstCol(this.getHighlightSegs(), colCnt); + var mirrorSegsByCol = splitSegsByFirstCol(this.getMirrorSegs(), colCnt); + var _b = computeFgSegPlacement(props.cells, props.fgEventSegs, props.dayMaxEvents, props.dayMaxEventRows, state.segHeights, state.maxContentHeight, colCnt, context.options.eventOrder), paddingBottoms = _b.paddingBottoms, segsByFirstCol = _b.segsByFirstCol, segsByEachCol = _b.segsByEachCol, segIsHidden = _b.segIsHidden, segTops = _b.segTops, segMarginTops = _b.segMarginTops, moreCnts = _b.moreCnts, moreTops = _b.moreTops; + var selectedInstanceHash = // TODO: messy way to compute this + (props.eventDrag && props.eventDrag.affectedInstances) || + (props.eventResize && props.eventResize.affectedInstances) || + {}; + return (createElement("tr", { ref: this.rootElRef }, + props.renderIntro && props.renderIntro(), + props.cells.map(function (cell, col) { + var normalFgNodes = _this.renderFgSegs(segsByFirstCol[col], segIsHidden, segTops, segMarginTops, selectedInstanceHash, props.todayRange); + var mirrorFgNodes = _this.renderFgSegs(mirrorSegsByCol[col], {}, segTops, // use same tops as real rendering + {}, {}, props.todayRange, Boolean(props.eventDrag), Boolean(props.eventResize), false // date-selecting (because mirror is never drawn for date selection) + ); + return (createElement(TableCell, { key: cell.key, elRef: _this.cellElRefs.createRef(cell.key), innerElRef: _this.frameElRefs.createRef(cell.key) /* FF problem, but okay to use for left/right. TODO: rename prop */, dateProfile: props.dateProfile, date: cell.date, showDayNumber: props.showDayNumbers, showWeekNumber: props.showWeekNumbers && col === 0, forceDayTop: props.showWeekNumbers /* even displaying weeknum for row, not necessarily day */, todayRange: props.todayRange, extraHookProps: cell.extraHookProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, moreCnt: moreCnts[col], buildMoreLinkText: props.buildMoreLinkText, onMoreClick: props.onMoreClick, segIsHidden: segIsHidden, moreMarginTop: moreTops[col] /* rename */, segsByEachCol: segsByEachCol[col], fgPaddingBottom: paddingBottoms[col], fgContentElRef: _this.fgElRefs.createRef(cell.key), fgContent: ( // Fragment scopes the keys + createElement(Fragment, null, + createElement(Fragment, null, normalFgNodes), + createElement(Fragment, null, mirrorFgNodes))), bgContent: ( // Fragment scopes the keys + createElement(Fragment, null, + _this.renderFillSegs(highlightSegsByCol[col], 'highlight'), + _this.renderFillSegs(businessHoursByCol[col], 'non-business'), + _this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))) })); + }))); + }; + TableRow.prototype.componentDidMount = function () { + this.updateSizing(true); + }; + TableRow.prototype.componentDidUpdate = function (prevProps, prevState) { + var currentProps = this.props; + this.updateSizing(!isPropsEqual(prevProps, currentProps)); + }; + TableRow.prototype.getHighlightSegs = function () { + var props = this.props; + if (props.eventDrag && props.eventDrag.segs.length) { // messy check + return props.eventDrag.segs; + } + else if (props.eventResize && props.eventResize.segs.length) { // messy check + return props.eventResize.segs; + } + else { + return props.dateSelectionSegs; + } + }; + TableRow.prototype.getMirrorSegs = function () { + var props = this.props; + if (props.eventResize && props.eventResize.segs.length) { // messy check + return props.eventResize.segs; + } + else { + return []; + } + }; + TableRow.prototype.renderFgSegs = function (segs, segIsHidden, // does NOT mean display:hidden + segTops, segMarginTops, selectedInstanceHash, todayRange, isDragging, isResizing, isDateSelecting) { + var context = this.context; + var eventSelection = this.props.eventSelection; + var framePositions = this.state.framePositions; + var defaultDisplayEventEnd = this.props.cells.length === 1; // colCnt === 1 + var nodes = []; + if (framePositions) { + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + var instanceId = seg.eventRange.instance.instanceId; + var isMirror = isDragging || isResizing || isDateSelecting; + var isSelected = selectedInstanceHash[instanceId]; + var isInvisible = segIsHidden[instanceId] || isSelected; + var isAbsolute = segIsHidden[instanceId] || isMirror || seg.firstCol !== seg.lastCol || !seg.isStart || !seg.isEnd; // TODO: simpler way? NOT DRY + var marginTop = void 0; + var top_1 = void 0; + var left = void 0; + var right = void 0; + if (isAbsolute) { + top_1 = segTops[instanceId]; + if (context.isRtl) { + right = 0; + left = framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol]; + } + else { + left = 0; + right = framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol]; + } + } + else { + marginTop = segMarginTops[instanceId]; + } + /* + known bug: events that are force to be list-item but span multiple days still take up space in later columns + */ + nodes.push(createElement("div", { className: 'fc-daygrid-event-harness' + (isAbsolute ? ' fc-daygrid-event-harness-abs' : ''), key: instanceId, ref: isMirror ? null : this.segHarnessRefs.createRef(instanceId + ':' + seg.firstCol) /* in print mode when in mult cols, could collide */, style: { + visibility: isInvisible ? 'hidden' : '', + marginTop: marginTop || '', + top: top_1 || '', + left: left || '', + right: right || '' + } }, hasListItemDisplay(seg) ? + createElement(TableListItemEvent, __assign({ seg: seg, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange))) : + createElement(TableBlockEvent, __assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange))))); + } + } + return nodes; + }; + TableRow.prototype.renderFillSegs = function (segs, fillType) { + var isRtl = this.context.isRtl; + var todayRange = this.props.todayRange; + var framePositions = this.state.framePositions; + var nodes = []; + if (framePositions) { + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + var leftRightCss = isRtl ? { + right: 0, + left: framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol] + } : { + left: 0, + right: framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol], + }; + nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: 'fc-daygrid-bg-harness', style: leftRightCss }, fillType === 'bg-event' ? + createElement(BgEvent, __assign({ seg: seg }, getSegMeta(seg, todayRange))) : + renderFill(fillType))); + } + } + return createElement.apply(void 0, __spreadArrays([Fragment, {}], nodes)); + }; + TableRow.prototype.updateSizing = function (isExternalSizingChange) { + var _a = this, props = _a.props, frameElRefs = _a.frameElRefs; + if (props.clientWidth !== null) { // positioning ready? + if (isExternalSizingChange) { + var frameEls = props.cells.map(function (cell) { return frameElRefs.currentMap[cell.key]; }); + if (frameEls.length) { + var originEl = this.rootElRef.current; + this.setState({ + framePositions: new PositionCache(originEl, frameEls, true, // isHorizontal + false) + }); + } + } + var limitByContentHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true; + this.setState({ + segHeights: this.computeSegHeights(), + maxContentHeight: limitByContentHeight ? this.computeMaxContentHeight() : null + }); + } + }; + TableRow.prototype.computeSegHeights = function () { + return mapHash(this.segHarnessRefs.currentMap, function (eventHarnessEl) { return (eventHarnessEl.getBoundingClientRect().height); }); + }; + TableRow.prototype.computeMaxContentHeight = function () { + var firstKey = this.props.cells[0].key; + var cellEl = this.cellElRefs.currentMap[firstKey]; + var fcContainerEl = this.fgElRefs.currentMap[firstKey]; + return cellEl.getBoundingClientRect().bottom - fcContainerEl.getBoundingClientRect().top; + }; + TableRow.prototype.getCellEls = function () { + var elMap = this.cellElRefs.currentMap; + return this.props.cells.map(function (cell) { return elMap[cell.key]; }); + }; + return TableRow; + }(DateComponent)); + TableRow.addStateEquality({ + segHeights: isPropsEqual + }); + + var PADDING_FROM_VIEWPORT = 10; + var SCROLL_DEBOUNCE = 10; + var Popover = /** @class */ (function (_super) { + __extends(Popover, _super); + function Popover() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.repositioner = new DelayedRunner(_this.updateSize.bind(_this)); + _this.handleRootEl = function (el) { + _this.rootEl = el; + if (_this.props.elRef) { + setRef(_this.props.elRef, el); + } + }; + // Triggered when the user clicks *anywhere* in the document, for the autoHide feature + _this.handleDocumentMousedown = function (ev) { + var onClose = _this.props.onClose; + // only hide the popover if the click happened outside the popover + if (onClose && !_this.rootEl.contains(ev.target)) { + onClose(); + } + }; + _this.handleDocumentScroll = function () { + _this.repositioner.request(SCROLL_DEBOUNCE); + }; + _this.handleCloseClick = function () { + var onClose = _this.props.onClose; + if (onClose) { + onClose(); + } + }; + return _this; + } + Popover.prototype.render = function () { + var theme = this.context.theme; + var props = this.props; + var classNames = [ + 'fc-popover', + theme.getClass('popover') + ].concat(props.extraClassNames || []); + return (createElement("div", __assign({ className: classNames.join(' ') }, props.extraAttrs, { ref: this.handleRootEl }), + createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') }, + createElement("span", { className: 'fc-popover-title' }, props.title), + createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), onClick: this.handleCloseClick })), + createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children))); + }; + Popover.prototype.componentDidMount = function () { + document.addEventListener('mousedown', this.handleDocumentMousedown); + document.addEventListener('scroll', this.handleDocumentScroll); + this.updateSize(); + }; + Popover.prototype.componentWillUnmount = function () { + document.removeEventListener('mousedown', this.handleDocumentMousedown); + document.removeEventListener('scroll', this.handleDocumentScroll); + }; + // TODO: adjust on window resize + /* + NOTE: the popover is position:fixed, so coordinates are relative to the viewport + NOTE: the PARENT calls this as well, on window resize. we would have wanted to use the repositioner, + but need to ensure that all other components have updated size first (for alignmentEl) + */ + Popover.prototype.updateSize = function () { + var _a = this.props, alignmentEl = _a.alignmentEl, topAlignmentEl = _a.topAlignmentEl; + var rootEl = this.rootEl; + if (!rootEl) { + return; // not sure why this was null, but we shouldn't let external components call updateSize() anyway + } + var dims = rootEl.getBoundingClientRect(); // only used for width,height + var alignment = alignmentEl.getBoundingClientRect(); + var top = topAlignmentEl ? topAlignmentEl.getBoundingClientRect().top : alignment.top; + top = Math.min(top, window.innerHeight - dims.height - PADDING_FROM_VIEWPORT); + top = Math.max(top, PADDING_FROM_VIEWPORT); + var left; + if (this.context.isRtl) { + left = alignment.right - dims.width; + } + else { + left = alignment.left; + } + left = Math.min(left, window.innerWidth - dims.width - PADDING_FROM_VIEWPORT); + left = Math.max(left, PADDING_FROM_VIEWPORT); + applyStyle(rootEl, { top: top, left: left }); + }; + return Popover; + }(BaseComponent)); + + var MorePopover = /** @class */ (function (_super) { + __extends(MorePopover, _super); + function MorePopover() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.handlePopoverEl = function (popoverEl) { + _this.popoverEl = popoverEl; + if (popoverEl) { + _this.context.registerInteractiveComponent(_this, { + el: popoverEl, + useEventCenter: false + }); + } + else { + _this.context.unregisterInteractiveComponent(_this); + } + }; + return _this; + } + MorePopover.prototype.render = function () { + var _a = this.context, options = _a.options, dateEnv = _a.dateEnv; + var props = this.props; + var date = props.date, hiddenInstances = props.hiddenInstances, todayRange = props.todayRange, dateProfile = props.dateProfile, selectedInstanceId = props.selectedInstanceId; + var title = dateEnv.format(date, options.dayPopoverFormat); + return (createElement(DayCellRoot, { date: date, dateProfile: dateProfile, todayRange: todayRange, elRef: this.handlePopoverEl }, function (rootElRef, dayClassNames, dataAttrs) { return (createElement(Popover, { elRef: rootElRef, title: title, extraClassNames: ['fc-more-popover'].concat(dayClassNames), extraAttrs: dataAttrs, onClose: props.onCloseClick, alignmentEl: props.alignmentEl, topAlignmentEl: props.topAlignmentEl }, + createElement(DayCellContent, { date: date, dateProfile: dateProfile, todayRange: todayRange }, function (innerElRef, innerContent) { return (innerContent && + createElement("div", { className: 'fc-more-popover-misc', ref: innerElRef }, innerContent)); }), + props.segs.map(function (seg) { + var instanceId = seg.eventRange.instance.instanceId; + return (createElement("div", { className: 'fc-daygrid-event-harness', key: instanceId, style: { + visibility: hiddenInstances[instanceId] ? 'hidden' : '' + } }, hasListItemDisplay(seg) ? + createElement(TableListItemEvent, __assign({ seg: seg, isDragging: false, isSelected: instanceId === selectedInstanceId, defaultDisplayEventEnd: false }, getSegMeta(seg, todayRange))) : + createElement(TableBlockEvent, __assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === selectedInstanceId, defaultDisplayEventEnd: false }, getSegMeta(seg, todayRange))))); + }))); })); + }; + MorePopover.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) { + var date = this.props.date; + if (positionLeft < elWidth && positionTop < elHeight) { + return { + component: this, + dateSpan: { + allDay: true, + range: { start: date, end: addDays(date, 1) } + }, + dayEl: this.popoverEl, + rect: { + left: 0, + top: 0, + right: elWidth, + bottom: elHeight + }, + layer: 1 + }; + } + }; + MorePopover.prototype.isPopover = function () { + return true; // gross + }; + return MorePopover; + }(DateComponent)); + + var Table = /** @class */ (function (_super) { + __extends(Table, _super); + function Table() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.splitBusinessHourSegs = memoize(splitSegsByRow); + _this.splitBgEventSegs = memoize(splitSegsByRow); + _this.splitFgEventSegs = memoize(splitSegsByRow); + _this.splitDateSelectionSegs = memoize(splitSegsByRow); + _this.splitEventDrag = memoize(splitInteractionByRow); + _this.splitEventResize = memoize(splitInteractionByRow); + _this.buildBuildMoreLinkText = memoize(buildBuildMoreLinkText); + _this.rowRefs = new RefMap(); + _this.state = { + morePopoverState: null + }; + _this.handleRootEl = function (rootEl) { + _this.rootEl = rootEl; + setRef(_this.props.elRef, rootEl); + }; + _this.handleMoreLinkClick = function (arg) { + var context = _this.context; + var dateEnv = context.dateEnv; + var clickOption = context.options.moreLinkClick; + function segForPublic(seg) { + var _a = seg.eventRange, def = _a.def, instance = _a.instance, range = _a.range; + return { + event: new EventApi(context, def, instance), + start: dateEnv.toDate(range.start), + end: dateEnv.toDate(range.end), + isStart: seg.isStart, + isEnd: seg.isEnd + }; + } + if (typeof clickOption === 'function') { + clickOption = clickOption({ + date: dateEnv.toDate(arg.date), + allDay: true, + allSegs: arg.allSegs.map(segForPublic), + hiddenSegs: arg.hiddenSegs.map(segForPublic), + jsEvent: arg.ev, + view: context.viewApi + }); // hack to handle void + } + if (!clickOption || clickOption === 'popover') { + _this.setState({ + morePopoverState: __assign(__assign({}, arg), { currentFgEventSegs: _this.props.fgEventSegs }) + }); + } + else if (typeof clickOption === 'string') { // a view name + context.calendarApi.zoomTo(arg.date, clickOption); + } + }; + _this.handleMorePopoverClose = function () { + _this.setState({ + morePopoverState: null + }); + }; + return _this; + } + Table.prototype.render = function () { + var _this = this; + var props = this.props; + var dateProfile = props.dateProfile, dayMaxEventRows = props.dayMaxEventRows, dayMaxEvents = props.dayMaxEvents, expandRows = props.expandRows; + var morePopoverState = this.state.morePopoverState; + var rowCnt = props.cells.length; + var businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt); + var bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt); + var fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt); + var dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt); + var eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt); + var eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt); + var buildMoreLinkText = this.buildBuildMoreLinkText(this.context.options.moreLinkText); + var limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true; + // if rows can't expand to fill fixed height, can't do balanced-height event limit + // TODO: best place to normalize these options? + if (limitViaBalanced && !expandRows) { + limitViaBalanced = false; + dayMaxEventRows = null; + dayMaxEvents = null; + } + var classNames = [ + 'fc-daygrid-body', + limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced', + expandRows ? '' : 'fc-daygrid-body-natural' // will height of one row depend on the others? + ]; + return (createElement("div", { className: classNames.join(' '), ref: this.handleRootEl, style: { + // these props are important to give this wrapper correct dimensions for interactions + // TODO: if we set it here, can we avoid giving to inner tables? + width: props.clientWidth, + minWidth: props.tableMinWidth + } }, + createElement(NowTimer, { unit: 'day' }, function (nowDate, todayRange) { return (createElement(Fragment, null, + createElement("table", { className: 'fc-scrollgrid-sync-table', style: { + width: props.clientWidth, + minWidth: props.tableMinWidth, + height: expandRows ? props.clientHeight : '' + } }, + props.colGroupNode, + createElement("tbody", null, props.cells.map(function (cells, row) { return (createElement(TableRow, { ref: _this.rowRefs.createRef(row), key: cells.length + ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */ + : row // in case there are no cells (like when resource view is loading) + , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, buildMoreLinkText: buildMoreLinkText, onMoreClick: _this.handleMoreLinkClick })); }))), + (!props.forPrint && morePopoverState && morePopoverState.currentFgEventSegs === props.fgEventSegs) && // clear popover on event mod + createElement(MorePopover, { date: morePopoverState.date, dateProfile: dateProfile, segs: morePopoverState.allSegs, alignmentEl: morePopoverState.dayEl, topAlignmentEl: rowCnt === 1 ? props.headerAlignElRef.current : null, onCloseClick: _this.handleMorePopoverClose, selectedInstanceId: props.eventSelection, hiddenInstances: // yuck + (props.eventDrag ? props.eventDrag.affectedInstances : null) || + (props.eventResize ? props.eventResize.affectedInstances : null) || + {}, todayRange: todayRange }))); }))); + }; + // Hit System + // ---------------------------------------------------------------------------------------------------- + Table.prototype.prepareHits = function () { + this.rowPositions = new PositionCache(this.rootEl, this.rowRefs.collect().map(function (rowObj) { return rowObj.getCellEls()[0]; }), // first cell el in each row. TODO: not optimal + false, true // vertical + ); + this.colPositions = new PositionCache(this.rootEl, this.rowRefs.currentMap[0].getCellEls(), // cell els in first row + true, // horizontal + false); + }; + Table.prototype.positionToHit = function (leftPosition, topPosition) { + var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions; + var col = colPositions.leftToIndex(leftPosition); + var row = rowPositions.topToIndex(topPosition); + if (row != null && col != null) { + return { + row: row, + col: col, + dateSpan: { + range: this.getCellRange(row, col), + allDay: true + }, + dayEl: this.getCellEl(row, col), + relativeRect: { + left: colPositions.lefts[col], + right: colPositions.rights[col], + top: rowPositions.tops[row], + bottom: rowPositions.bottoms[row] + } + }; + } + }; + Table.prototype.getCellEl = function (row, col) { + return this.rowRefs.currentMap[row].getCellEls()[col]; // TODO: not optimal + }; + Table.prototype.getCellRange = function (row, col) { + var start = this.props.cells[row][col].date; + var end = addDays(start, 1); + return { start: start, end: end }; + }; + return Table; + }(DateComponent)); + function buildBuildMoreLinkText(moreLinkTextInput) { + if (typeof moreLinkTextInput === 'function') { + return moreLinkTextInput; + } + else { + return function (num) { + return "+" + num + " " + moreLinkTextInput; + }; + } + } + function isSegAllDay(seg) { + return seg.eventRange.def.allDay; + } + + var DayTable = /** @class */ (function (_super) { + __extends(DayTable, _super); + function DayTable() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.slicer = new DayTableSlicer(); + _this.tableRef = createRef(); + _this.handleRootEl = function (rootEl) { + if (rootEl) { + _this.context.registerInteractiveComponent(_this, { el: rootEl }); + } + else { + _this.context.unregisterInteractiveComponent(_this); + } + }; + return _this; + } + DayTable.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + return (createElement(Table, __assign({ ref: this.tableRef, elRef: this.handleRootEl }, this.slicer.sliceProps(props, props.dateProfile, props.nextDayThreshold, context, props.dayTableModel), { dateProfile: props.dateProfile, cells: props.dayTableModel.cells, colGroupNode: props.colGroupNode, tableMinWidth: props.tableMinWidth, renderRowIntro: props.renderRowIntro, dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, showWeekNumbers: props.showWeekNumbers, expandRows: props.expandRows, headerAlignElRef: props.headerAlignElRef, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint }))); + }; + DayTable.prototype.prepareHits = function () { + this.tableRef.current.prepareHits(); + }; + DayTable.prototype.queryHit = function (positionLeft, positionTop) { + var rawHit = this.tableRef.current.positionToHit(positionLeft, positionTop); + if (rawHit) { + return { + component: this, + dateSpan: rawHit.dateSpan, + dayEl: rawHit.dayEl, + rect: { + left: rawHit.relativeRect.left, + right: rawHit.relativeRect.right, + top: rawHit.relativeRect.top, + bottom: rawHit.relativeRect.bottom + }, + layer: 0 + }; + } + }; + return DayTable; + }(DateComponent)); + var DayTableSlicer = /** @class */ (function (_super) { + __extends(DayTableSlicer, _super); + function DayTableSlicer() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.forceDayIfListItem = true; + return _this; + } + DayTableSlicer.prototype.sliceRange = function (dateRange, dayTableModel) { + return dayTableModel.sliceRange(dateRange); + }; + return DayTableSlicer; + }(Slicer)); + + var DayTableView = /** @class */ (function (_super) { + __extends(DayTableView, _super); + function DayTableView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.buildDayTableModel = memoize(buildDayTableModel); + _this.headerRef = createRef(); + _this.tableRef = createRef(); + return _this; + } + DayTableView.prototype.render = function () { + var _this = this; + var _a = this.context, options = _a.options, dateProfileGenerator = _a.dateProfileGenerator; + var props = this.props; + var dayTableModel = this.buildDayTableModel(props.dateProfile, dateProfileGenerator); + var headerContent = options.dayHeaders && + createElement(DayHeader, { ref: this.headerRef, dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: dayTableModel.rowCnt === 1 }); + var bodyContent = function (contentArg) { return (createElement(DayTable, { ref: _this.tableRef, dateProfile: props.dateProfile, dayTableModel: dayTableModel, businessHours: props.businessHours, dateSelection: props.dateSelection, eventStore: props.eventStore, eventUiBases: props.eventUiBases, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, nextDayThreshold: options.nextDayThreshold, colGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows, showWeekNumbers: options.weekNumbers, expandRows: !props.isHeightAuto, headerAlignElRef: _this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint })); }; + return options.dayMinWidth + ? this.renderHScrollLayout(headerContent, bodyContent, dayTableModel.colCnt, options.dayMinWidth) + : this.renderSimpleLayout(headerContent, bodyContent); + }; + return DayTableView; + }(TableView)); + function buildDayTableModel(dateProfile, dateProfileGenerator) { + var daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator); + return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit)); + } + + var TableDateProfileGenerator = /** @class */ (function (_super) { + __extends(TableDateProfileGenerator, _super); + function TableDateProfileGenerator() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Computes the date range that will be rendered. + TableDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { + var dateEnv = this.props.dateEnv; + var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay); + var start = renderRange.start; + var end = renderRange.end; + var endOfWeek; + // year and month views should be aligned with weeks. this is already done for week + if (/^(year|month)$/.test(currentRangeUnit)) { + start = dateEnv.startOfWeek(start); + // make end-of-week if not already + endOfWeek = dateEnv.startOfWeek(end); + if (endOfWeek.valueOf() !== end.valueOf()) { + end = addWeeks(endOfWeek, 1); + } + } + // ensure 6 weeks + if (this.props.monthMode && + this.props.fixedWeekCount) { + var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays + diffWeeks(start, end)); + end = addWeeks(end, 6 - rowCnt); + } + return { start: start, end: end }; + }; + return TableDateProfileGenerator; + }(DateProfileGenerator)); + + var OPTION_REFINERS = { + moreLinkClick: identity, + moreLinkClassNames: identity, + moreLinkContent: identity, + moreLinkDidMount: identity, + moreLinkWillUnmount: identity, + }; + + var dayGridPlugin = createPlugin({ + initialView: 'dayGridMonth', + optionRefiners: OPTION_REFINERS, + views: { + dayGrid: { + component: DayTableView, + dateProfileGeneratorClass: TableDateProfileGenerator + }, + dayGridDay: { + type: 'dayGrid', + duration: { days: 1 } + }, + dayGridWeek: { + type: 'dayGrid', + duration: { weeks: 1 } + }, + dayGridMonth: { + type: 'dayGrid', + duration: { months: 1 }, + monthMode: true, + fixedWeekCount: true + } + } + }); + + var AllDaySplitter = /** @class */ (function (_super) { + __extends(AllDaySplitter, _super); + function AllDaySplitter() { + return _super !== null && _super.apply(this, arguments) || this; + } + AllDaySplitter.prototype.getKeyInfo = function () { + return { + allDay: {}, + timed: {} + }; + }; + AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) { + if (dateSpan.allDay) { + return ['allDay']; + } + else { + return ['timed']; + } + }; + AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) { + if (!eventDef.allDay) { + return ['timed']; + } + else if (hasBgRendering(eventDef)) { + return ['timed', 'allDay']; + } + else { + return ['allDay']; + } + }; + return AllDaySplitter; + }(Splitter)); + + var TimeColsSlatsCoords = /** @class */ (function () { + function TimeColsSlatsCoords(positions, dateProfile, slatMetas) { + this.positions = positions; + this.dateProfile = dateProfile; + this.slatMetas = slatMetas; + } + TimeColsSlatsCoords.prototype.safeComputeTop = function (date) { + var dateProfile = this.dateProfile; + if (rangeContainsMarker(dateProfile.currentRange, date)) { + var startOfDayDate = startOfDay(date); + var timeMs = date.valueOf() - startOfDayDate.valueOf(); + if (timeMs >= asRoughMs(dateProfile.slotMinTime) && + timeMs < asRoughMs(dateProfile.slotMaxTime)) { + return this.computeTimeTop(createDuration(timeMs)); + } + } + }; + // Computes the top coordinate, relative to the bounds of the grid, of the given date. + // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight. + TimeColsSlatsCoords.prototype.computeDateTop = function (when, startOfDayDate) { + if (!startOfDayDate) { + startOfDayDate = startOfDay(when); + } + return this.computeTimeTop(createDuration(when.valueOf() - startOfDayDate.valueOf())); + }; + // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration). + // This is a makeshify way to compute the time-top. Assumes all slatMetas dates are uniform. + // Eventually allow computation with arbirary slat dates. + TimeColsSlatsCoords.prototype.computeTimeTop = function (duration) { + var _a = this, positions = _a.positions, dateProfile = _a.dateProfile, slatMetas = _a.slatMetas; + var len = positions.els.length; + var slotDurationMs = slatMetas[1].date.valueOf() - slatMetas[0].date.valueOf(); // we assume dates are uniform + var slatCoverage = (duration.milliseconds - asRoughMs(dateProfile.slotMinTime)) / slotDurationMs; // floating-point value of # of slots covered + var slatIndex; + var slatRemainder; + // compute a floating-point number for how many slats should be progressed through. + // from 0 to number of slats (inclusive) + // constrained because slotMinTime/slotMaxTime might be customized. + slatCoverage = Math.max(0, slatCoverage); + slatCoverage = Math.min(len, slatCoverage); + // an integer index of the furthest whole slat + // from 0 to number slats (*exclusive*, so len-1) + slatIndex = Math.floor(slatCoverage); + slatIndex = Math.min(slatIndex, len - 1); + // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition. + // could be 1.0 if slatCoverage is covering *all* the slots + slatRemainder = slatCoverage - slatIndex; + return positions.tops[slatIndex] + + positions.getHeight(slatIndex) * slatRemainder; + }; + return TimeColsSlatsCoords; + }()); + + // potential nice values for the slot-duration and interval-duration + // from largest to smallest + var STOCK_SUB_DURATIONS = [ + { hours: 1 }, + { minutes: 30 }, + { minutes: 15 }, + { seconds: 30 }, + { seconds: 15 } + ]; + /* + for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL. + */ + var TimeColsSlats = /** @class */ (function (_super) { + __extends(TimeColsSlats, _super); + function TimeColsSlats() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.rootElRef = createRef(); + _this.slatElRefs = new RefMap(); + return _this; + } + TimeColsSlats.prototype.render = function () { + var _a = this, props = _a.props, context = _a.context; + return (createElement("div", { className: 'fc-timegrid-slots', ref: this.rootElRef }, + createElement("table", { className: context.theme.getClass('table'), style: { + minWidth: props.tableMinWidth, + width: props.clientWidth, + height: props.minHeight + } }, + props.tableColGroupNode /* relies on there only being a single
",e.querySelector("table").style.height="100px",e.querySelector("div").style.height="100%",document.body.appendChild(e);var t=e.querySelector("div").offsetHeight>0;return document.body.removeChild(e),t}()),nr}var dr={defs:{},instances:{}},pr=function(){function e(){this.getKeysForEventDefs=it(this._getKeysForEventDefs),this.splitDateSelection=it(this._splitDateSpan),this.splitEventStore=it(this._splitEventStore),this.splitIndividualUi=it(this._splitIndividualUi),this.splitEventDrag=it(this._splitInteraction),this.splitEventResize=it(this._splitInteraction),this.eventUiBuilders={}}return e.prototype.splitProps=function(e){var t=this,n=this.getKeyInfo(e),r=this.getKeysForEventDefs(e.eventStore),o=this.splitDateSelection(e.dateSelection),i=this.splitIndividualUi(e.eventUiBases,r),a=this.splitEventStore(e.eventStore,r),s=this.splitEventDrag(e.eventDrag),l=this.splitEventResize(e.eventResize),u={};for(var c in this.eventUiBuilders=Oe(n,(function(e,n){return t.eventUiBuilders[n]||it(fr)})),n){var d=n[c],p=a[c]||dr,f=this.eventUiBuilders[c];u[c]={businessHours:d.businessHours||e.businessHours,dateSelection:o[c]||null,eventStore:p,eventUiBases:f(e.eventUiBases[""],d.ui,i[c]),eventSelection:p.instances[e.eventSelection]?e.eventSelection:"",eventDrag:s[c]||null,eventResize:l[c]||null}}return u},e.prototype._splitDateSpan=function(e){var t={};if(e)for(var n=0,r=this.getKeysForDateSpan(e);nn:!!t&&e>=t.end)}}function vr(e,t){var n=["fc-day","fc-day-"+fe[e.dow]];return e.isDisabled?n.push("fc-day-disabled"):(e.isToday&&(n.push("fc-day-today"),n.push(t.getClass("today"))),e.isPast&&n.push("fc-day-past"),e.isFuture&&n.push("fc-day-future"),e.isOther&&n.push("fc-day-other")),n}function gr(e,t){return void 0===t&&(t="day"),JSON.stringify({date:tt(e),type:t})}var mr,yr=null;function Er(){return null===yr&&(yr=function(){var e=document.createElement("div");j(e,{position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}),e.innerHTML="
",document.body.appendChild(e);var t=e.firstChild.getBoundingClientRect().left>e.getBoundingClientRect().left;return W(e),t}()),yr}function Sr(){return mr||(mr=function(){var e=document.createElement("div");e.style.overflow="scroll",document.body.appendChild(e);var t=Dr(e);return document.body.removeChild(e),t}()),mr}function Dr(e){return{x:e.offsetHeight-e.clientHeight,y:e.offsetWidth-e.clientWidth}}function br(e,t){void 0===t&&(t=!1);var n=window.getComputedStyle(e),r=parseInt(n.borderLeftWidth,10)||0,o=parseInt(n.borderRightWidth,10)||0,i=parseInt(n.borderTopWidth,10)||0,a=parseInt(n.borderBottomWidth,10)||0,s=Dr(e),l=s.y-r-o,u={borderLeft:r,borderRight:o,borderTop:i,borderBottom:a,scrollbarBottom:s.x-i-a,scrollbarLeft:0,scrollbarRight:0};return Er()&&"rtl"===n.direction?u.scrollbarLeft=l:u.scrollbarRight=l,t&&(u.paddingLeft=parseInt(n.paddingLeft,10)||0,u.paddingRight=parseInt(n.paddingRight,10)||0,u.paddingTop=parseInt(n.paddingTop,10)||0,u.paddingBottom=parseInt(n.paddingBottom,10)||0),u}function Cr(e,t,n){void 0===t&&(t=!1);var r=n?e.getBoundingClientRect():wr(e),o=br(e,t),i={left:r.left+o.borderLeft+o.scrollbarLeft,right:r.right-o.borderRight-o.scrollbarRight,top:r.top+o.borderTop,bottom:r.bottom-o.borderBottom-o.scrollbarBottom};return t&&(i.left+=o.paddingLeft,i.right-=o.paddingRight,i.top+=o.paddingTop,i.bottom-=o.paddingBottom),i}function wr(e){var t=e.getBoundingClientRect();return{left:t.left+window.pageXOffset,top:t.top+window.pageYOffset,right:t.right+window.pageXOffset,bottom:t.bottom+window.pageYOffset}}function Rr(e){for(var t=[];e instanceof HTMLElement;){var n=window.getComputedStyle(e);if("fixed"===n.position)break;/(auto|scroll)/.test(n.overflow+n.overflowY+n.overflowX)&&t.push(e),e=e.parentNode}return t}function Tr(e,t,n){var r=!1,o=function(){r||(r=!0,t.apply(this,arguments))},i=function(){r||(r=!0,n&&n.apply(this,arguments))},a=e(o,i);a&&"function"==typeof a.then&&a.then(o,i)}var kr=function(){function e(){this.handlers={},this.thisContext=null}return e.prototype.setThisContext=function(e){this.thisContext=e},e.prototype.setOptions=function(e){this.options=e},e.prototype.on=function(e,t){!function(e,t,n){(e[t]||(e[t]=[])).push(n)}(this.handlers,e,t)},e.prototype.off=function(e,t){!function(e,t,n){n?e[t]&&(e[t]=e[t].filter((function(e){return e!==n}))):delete e[t]}(this.handlers,e,t)},e.prototype.trigger=function(e){for(var t=[],n=1;n=n[t]&&e=n[t]&&e0},e.prototype.canScrollHorizontally=function(){return this.getMaxScrollLeft()>0},e.prototype.canScrollUp=function(){return this.getScrollTop()>0},e.prototype.canScrollDown=function(){return this.getScrollTop()0},e.prototype.canScrollRight=function(){return this.getScrollLeft()=c.end?new Date(c.end.valueOf()-1):u),o=this.buildCurrentRangeInfo(e,t),i=/^(year|month|week|day)$/.test(o.unit),a=this.buildRenderRange(this.trimHiddenDays(o.range),o.unit,i),s=a=this.trimHiddenDays(a),d.showNonCurrentDates||(s=nn(s,o.range)),s=nn(s=this.adjustActiveRange(s),r),l=on(o.range,r),{validRange:r,currentRange:o.range,currentRangeUnit:o.unit,isRangeAllDay:i,activeRange:s,renderRange:a,slotMinTime:d.slotMinTime,slotMaxTime:d.slotMaxTime,isValid:l,dateIncrement:this.buildDateIncrement(o.duration)}},e.prototype.buildValidRange=function(){var e=this.props.validRangeInput,t="function"==typeof e?e.call(this.props.calendarApi,this.nowDate):e;return this.refineRange(t)||{start:null,end:null}},e.prototype.buildCurrentRangeInfo=function(e,t){var n,r=this.props,o=null,i=null,a=null;return r.duration?(o=r.duration,i=r.durationUnit,a=this.buildRangeFromDuration(e,t,o,i)):(n=this.props.dayCount)?(i="day",a=this.buildRangeFromDayCount(e,t,n)):(a=this.buildCustomVisibleRange(e))?i=r.dateEnv.greatestWholeUnit(a.start,a.end).unit:(i=et(o=this.getFallbackDuration()).unit,a=this.buildRangeFromDuration(e,t,o,i)),{duration:o,unit:i,range:a}},e.prototype.getFallbackDuration=function(){return Ye({day:1})},e.prototype.adjustActiveRange=function(e){var t=this.props,n=t.dateEnv,r=t.usesMinMaxTime,o=t.slotMinTime,i=t.slotMaxTime,a=e.start,s=e.end;return r&&(Je(o)<0&&(a=be(a),a=n.add(a,o)),Je(i)>1&&(s=ve(s=be(s),-1),s=n.add(s,i))),{start:a,end:s}},e.prototype.buildRangeFromDuration=function(e,t,n,r){var o,i,a,s=this.props,l=s.dateEnv,u=s.dateAlignment;if(!u){var c=this.props.dateIncrement;u=c&&$e(c)<$e(n)?et(c).unit:r}function d(){o=l.startOf(e,u),i=l.add(o,n),a={start:o,end:i}}return Je(n)<=1&&this.isHiddenDay(o)&&(o=be(o=this.skipHiddenDays(o,t))),d(),this.trimHiddenDays(a)||(e=this.skipHiddenDays(e,t),d()),a},e.prototype.buildRangeFromDayCount=function(e,t,n){var r,o=this.props,i=o.dateEnv,a=o.dateAlignment,s=0,l=e;a&&(l=i.startOf(l,a)),l=be(l),r=l=this.skipHiddenDays(l,t);do{r=ve(r,1),this.isHiddenDay(r)||s++}while(se.fetchRange.end:!e.latestFetchId}(e,t,n)})),t,n)}function xo(e,t,n,r){var o={};for(var i in e){var a=e[i];t[i]?o[i]=_o(a,n,r):o[i]=a}return o}function _o(e,t,n){var o=n.options,i=n.calendarApi,a=n.pluginHooks.eventSourceDefs[e.sourceDefId],s=$();return a.fetch({eventSource:e,range:t,context:n},(function(r){var a=r.rawEvents;o.eventSourceSuccess&&(a=o.eventSourceSuccess.call(i,a,r.xhr)||a),e.success&&(a=e.success.call(i,a,r.xhr)||a),n.dispatch({type:"RECEIVE_EVENTS",sourceId:e.sourceId,fetchId:s,fetchRange:t,rawEvents:a})}),(function(r){console.warn(r.message,r),o.eventSourceFailure&&o.eventSourceFailure.call(i,r),e.failure&&e.failure(r),n.dispatch({type:"RECEIVE_EVENT_ERROR",sourceId:e.sourceId,fetchId:s,fetchRange:t,error:r})})),r(r({},e),{isFetching:!0,latestFetchId:s})}function Io(e,t){return He(e,(function(e){return Po(e,t)}))}function Po(e,t){return!t.pluginHooks.eventSourceDefs[e.sourceDefId].ignoreRange}function No(e,t){switch(t.type){case"UNSELECT_DATES":return null;case"SELECT_DATES":return t.selection;default:return e}}function Ho(e,t){switch(t.type){case"UNSELECT_EVENT":return"";case"SELECT_EVENT":return t.eventInstanceId;default:return e}}function Oo(e,t){var n;switch(t.type){case"UNSET_EVENT_DRAG":return null;case"SET_EVENT_DRAG":return{affectedEvents:(n=t.state).affectedEvents,mutatedEvents:n.mutatedEvents,isEvent:n.isEvent};default:return e}}function Ao(e,t){var n;switch(t.type){case"UNSET_EVENT_RESIZE":return null;case"SET_EVENT_RESIZE":return{affectedEvents:(n=t.state).affectedEvents,mutatedEvents:n.mutatedEvents,isEvent:n.isEvent};default:return e}}function Uo(e,t,n,r,o){var i=[];return{headerToolbar:e.headerToolbar?Lo(e.headerToolbar,e,t,n,r,o,i):null,footerToolbar:e.footerToolbar?Lo(e.footerToolbar,e,t,n,r,o,i):null,viewsWithButtons:i}}function Lo(e,t,n,r,o,i,a){return Oe(e,(function(e){return function(e,t,n,r,o,i,a){var s="rtl"===t.direction,l=t.customButtons||{},u=n.buttonText||{},c=t.buttonText||{};return(e?e.split(" "):[]).map((function(e){return e.split(",").map((function(e){if("title"===e)return{buttonName:e};var t,n=void 0,d=void 0,p=void 0,f=void 0;return(t=l[e])?(d=function(e){t.click&&t.click.call(e.target,e,e.target)},(p=r.getCustomButtonIconClass(t))||(p=r.getIconClass(e,s))||(f=t.text)):(n=o[e])?(a.push(e),d=function(){i.changeView(e)},(f=n.buttonTextOverride)||(p=r.getIconClass(e,s))||(f=n.buttonTextDefault)):i[e]&&(d=function(){i[e]()},(f=u[e])||(p=r.getIconClass(e,s))||(f=c[e])),{buttonName:e,buttonClick:d,buttonIcon:p,buttonText:f}}))}))}(e,t,n,r,o,i,a)}))}function Wo(e,t,n,r,o){var i=null;"GET"===(e=e.toUpperCase())?t=function(e,t){return e+(-1===e.indexOf("?")?"?":"&")+Vo(t)}(t,n):i=Vo(n);var a=new XMLHttpRequest;a.open(e,t,!0),"GET"!==e&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),a.onload=function(){if(a.status>=200&&a.status<400){var e=!1,t=void 0;try{t=JSON.parse(a.responseText),e=!0}catch(e){}e?r(t,a):o("Failure parsing JSON",a)}else o("Request failed",a)},a.onerror=function(){o("Request failed",a)},a.send(i)}function Vo(e){var t=[];for(var n in e)t.push(encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t.join("&")}function zo(e,t){for(var n=Ue(t.getCurrentData().eventSources),r=[],o=0,i=e;o1?{year:"numeric",month:"short",day:"numeric"}:{year:"numeric",month:"long",day:"numeric"}}(e)),{isEndExclusive:e.isRangeAllDay,defaultSeparator:t.titleRangeSeparator})}var Zo=function(){function e(e){var t=this;this.computeOptionsData=it(this._computeOptionsData),this.computeCurrentViewData=it(this._computeCurrentViewData),this.organizeRawLocales=it($n),this.buildLocale=it(Qn),this.buildPluginHooks=ao(),this.buildDateEnv=it(Xo),this.buildTheme=it(Ko),this.parseToolbars=it(Uo),this.buildViewSpecs=it(Do),this.buildDateProfileGenerator=at(Jo),this.buildViewApi=it($o),this.buildViewUiProps=at(ti),this.buildEventUiBySource=it(Qo,Le),this.buildEventUiBases=it(ei),this.parseContextBusinessHours=at(ni),this.buildTitle=it(Yo),this.emitter=new kr,this.actionRunner=new qo(this._handleAction.bind(this),this.updateData.bind(this)),this.currentCalendarOptionsInput={},this.currentCalendarOptionsRefined={},this.currentViewOptionsInput={},this.currentViewOptionsRefined={},this.currentCalendarOptionsRefiners={},this.getCurrentData=function(){return t.data},this.dispatch=function(e){t.actionRunner.request(e)},this.props=e,this.actionRunner.pause();var n={},o=this.computeOptionsData(e.optionOverrides,n,e.calendarApi),i=o.calendarOptions.initialView||o.pluginHooks.initialView,a=this.computeCurrentViewData(i,o,e.optionOverrides,n);e.calendarApi.currentDataManager=this,this.emitter.setThisContext(e.calendarApi),this.emitter.setOptions(a.options);var s,l,u,c=(s=o.calendarOptions,l=o.dateEnv,null!=(u=s.initialDate)?l.createMarker(u):Wn(s.now,l)),d=a.dateProfileGenerator.build(c);sn(d.activeRange,c)||(c=d.currentRange.start);for(var p={dateEnv:o.dateEnv,options:o.calendarOptions,pluginHooks:o.pluginHooks,calendarApi:e.calendarApi,dispatch:this.dispatch,emitter:this.emitter,getCurrentData:this.getCurrentData},f=0,h=o.pluginHooks.contextInit;f1){var m=a&&n.getClass("buttonGroup")||"";return Hr.apply(void 0,o(["div",{className:m}],i))}return i[0]},t}(jr),gi=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.state={availableWidth:null},t.handleEl=function(e){t.el=e,Yr(t.props.elRef,e),t.updateAvailableWidth()},t.handleResize=function(){t.updateAvailableWidth()},t}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.state,n=e.aspectRatio,r=["fc-view-harness",n||e.liquid||e.height?"fc-view-harness-active":"fc-view-harness-passive"],o="",i="";return n?null!==t.availableWidth?o=t.availableWidth/n:i=1/n*100+"%":o=e.height||"",Hr("div",{ref:this.handleEl,onClick:e.onClick,className:r.join(" "),style:{height:o,paddingBottom:i}},e.children)},t.prototype.componentDidMount=function(){this.context.addResizeHandler(this.handleResize)},t.prototype.componentWillUnmount=function(){this.context.removeResizeHandler(this.handleResize)},t.prototype.updateAvailableWidth=function(){this.el&&this.props.aspectRatio&&this.setState({availableWidth:this.el.offsetWidth})},t}(jr),mi=function(e){function t(t){var n=e.call(this,t)||this;return n.handleSegClick=function(e,t){var r=n.component,o=r.context,i=dn(t);if(i&&r.isValidSegDownEl(e.target)){var a=V(e.target,".fc-event-forced-url"),s=a?a.querySelector("a[href]").href:"";o.emitter.trigger("eventClick",{el:t,event:new zn(r.context,i.eventRange.def,i.eventRange.instance),jsEvent:e,view:o.viewApi}),s&&!e.defaultPrevented&&(window.location.href=s)}},n.destroy=Z(t.el,"click",".fc-event",n.handleSegClick),n}return n(t,e),t}(ai),yi=function(e){function t(t){var n,r,o,i,a,s=e.call(this,t)||this;return s.handleEventElRemove=function(e){e===s.currentSegEl&&s.handleSegLeave(null,s.currentSegEl)},s.handleSegEnter=function(e,t){dn(t)&&(s.currentSegEl=t,s.triggerEvent("eventMouseEnter",e,t))},s.handleSegLeave=function(e,t){s.currentSegEl&&(s.currentSegEl=null,s.triggerEvent("eventMouseLeave",e,t))},s.removeHoverListeners=(n=t.el,r=".fc-event",o=s.handleSegEnter,i=s.handleSegLeave,Z(n,"mouseover",r,(function(e,t){if(t!==a){a=t,o(e,t);var n=function(e){a=null,i(e,t),t.removeEventListener("mouseleave",n)};t.addEventListener("mouseleave",n)}}))),s}return n(t,e),t.prototype.destroy=function(){this.removeHoverListeners()},t.prototype.triggerEvent=function(e,t,n){var r=this.component,o=r.context,i=dn(n);t&&!r.isValidSegDownEl(t.target)||o.emitter.trigger(e,{el:n,event:new zn(o,i.eventRange.def,i.eventRange.instance),jsEvent:t,view:o.viewApi})},t}(ai),Ei=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.buildViewContext=it(Fr),t.buildViewPropTransformers=it(Di),t.buildToolbarProps=it(Si),t.handleNavLinkClick=Y("a[data-navlink]",t._handleNavLinkClick.bind(t)),t.headerRef=Ar(),t.footerRef=Ar(),t.interactionsStore={},t.registerInteractiveComponent=function(e,n){var r=si(e,n),o=[mi,yi].concat(t.props.pluginHooks.componentInteractions).map((function(e){return new e(r)}));t.interactionsStore[e.uid]=o,ui[e.uid]=r},t.unregisterInteractiveComponent=function(e){for(var n=0,r=t.interactionsStore[e.uid];n10?{weekday:"short"}:t>1?{weekday:"short",month:"numeric",day:"numeric",omitCommas:!0}:{weekday:"long"})}var wi="fc-col-header-cell",Ri=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.render=function(){var e=this.context,t=e.dateEnv,n=e.options,o=e.theme,i=e.viewApi,a=this.props,s=a.date,l=a.dateProfile,u=hr(s,a.todayRange,null,l),c=[wi].concat(vr(u,o)),d=t.format(s,a.dayHeaderFormat),p=n.navLinks&&!u.isDisabled&&a.colCnt>1?{"data-navlink":gr(s),tabIndex:0}:{},f=r(r(r({date:t.toDate(s),view:i},a.extraHookProps),{text:d}),u);return Hr(uo,{hookProps:f,classNames:n.dayHeaderClassNames,content:n.dayHeaderContent,defaultContent:ki,didMount:n.dayHeaderDidMount,willUnmount:n.dayHeaderWillUnmount},(function(e,t,n,o){return Hr("th",r({ref:e,className:c.concat(t).join(" "),"data-date":u.isDisabled?void 0:tt(s),colSpan:a.colSpan},a.extraDataAttrs),Hr("div",{className:"fc-scrollgrid-sync-inner"},!u.isDisabled&&Hr("a",r({ref:n,className:["fc-col-header-cell-cushion",a.isSticky?"fc-sticky":""].join(" ")},p),o)))}))},t}(jr),Ti=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.context,n=t.dateEnv,o=t.theme,i=t.viewApi,a=t.options,s=ve(new Date(2592e5),e.dow),l={dow:e.dow,isDisabled:!1,isFuture:!1,isPast:!1,isToday:!1,isOther:!1},u=[wi].concat(vr(l,o),e.extraClassNames||[]),c=n.format(s,e.dayHeaderFormat),d=r(r(r(r({date:s},l),{view:i}),e.extraHookProps),{text:c});return Hr(uo,{hookProps:d,classNames:a.dayHeaderClassNames,content:a.dayHeaderContent,defaultContent:ki,didMount:a.dayHeaderDidMount,willUnmount:a.dayHeaderWillUnmount},(function(t,n,o,i){return Hr("th",r({ref:t,className:u.concat(n).join(" "),colSpan:e.colSpan},e.extraDataAttrs),Hr("div",{className:"fc-scrollgrid-sync-inner"},Hr("a",{className:["fc-col-header-cell-cushion",e.isSticky?"fc-sticky":""].join(" "),ref:o},i)))}))},t}(jr);function ki(e){return e.text}var Mi=function(e){function t(t,n){var r=e.call(this,t,n)||this;return r.initialNowDate=Wn(n.options.now,n.dateEnv),r.initialNowQueriedMs=(new Date).valueOf(),r.state=r.computeTiming().currentState,r}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.state;return e.children(t.nowDate,t.todayRange)},t.prototype.componentDidMount=function(){this.setTimeout()},t.prototype.componentDidUpdate=function(e){e.unit!==this.props.unit&&(this.clearTimeout(),this.setTimeout())},t.prototype.componentWillUnmount=function(){this.clearTimeout()},t.prototype.computeTiming=function(){var e=this.props,t=this.context,n=ge(this.initialNowDate,(new Date).valueOf()-this.initialNowQueriedMs),r=t.dateEnv.startOf(n,e.unit),o=t.dateEnv.add(r,Ye(1,e.unit)),i=o.valueOf()-n.valueOf();return{currentState:{nowDate:r,todayRange:xi(r)},nextState:{nowDate:o,todayRange:xi(o)},waitMs:i}},t.prototype.setTimeout=function(){var e=this,t=this.computeTiming(),n=t.nextState,r=t.waitMs;this.timeoutId=setTimeout((function(){e.setState(n,(function(){e.setTimeout()}))}),r)},t.prototype.clearTimeout=function(){this.timeoutId&&clearTimeout(this.timeoutId)},t.contextType=zr,t}(Nr);function xi(e){var t=be(e);return{start:t,end:ve(t,1)}}var _i=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.createDayHeaderFormatter=it(Ii),t}return n(t,e),t.prototype.render=function(){var e=this.context,t=this.props,n=t.dates,r=t.dateProfile,o=t.datesRepDistinctDays,i=t.renderIntro,a=this.createDayHeaderFormatter(e.options.dayHeaderFormat,o,n.length);return Hr(Mi,{unit:"day"},(function(e,t){return Hr("tr",null,i&&i(),n.map((function(e){return o?Hr(Ri,{key:e.toISOString(),date:e,dateProfile:r,todayRange:t,colCnt:n.length,dayHeaderFormat:a}):Hr(Ti,{key:e.getUTCDay(),dow:e.getUTCDay(),dayHeaderFormat:a})})))}))},t}(jr);function Ii(e,t,n){return e||Ci(t,n)}var Pi=function(){function e(e,t){for(var n=e.start,r=e.end,o=[],i=[],a=-1;n=t.length?t[t.length-1]+1:t[n]},e}(),Ni=function(){function e(e,t){var n,r,o,i=e.dates;if(t){for(r=i[0].getUTCDay(),n=1;nt)return!0}return!1},t.prototype.needsYScrolling=function(){if(Ai.test(this.props.overflowY))return!1;for(var e=this.el,t=this.el.getBoundingClientRect().height-this.getXScrollbarWidth(),n=e.children,r=0;rt)return!0}return!1},t.prototype.getXScrollbarWidth=function(){return Ai.test(this.props.overflowX)?0:this.el.offsetHeight-this.el.clientHeight},t.prototype.getYScrollbarWidth=function(){return Ai.test(this.props.overflowY)?0:this.el.offsetWidth-this.el.clientWidth},t}(jr),Li=function(){function e(e){var t=this;this.masterCallback=e,this.currentMap={},this.depths={},this.callbackMap={},this.handleValue=function(e,n){var r=t,o=r.depths,i=r.currentMap,a=!1,s=!1;null!==e?(a=n in i,i[n]=e,o[n]=(o[n]||0)+1,s=!0):0==--o[n]&&(delete i[n],delete t.callbackMap[n],a=!0),t.masterCallback&&(a&&t.masterCallback(null,String(n)),s&&t.masterCallback(e,String(n)))}}return e.prototype.createRef=function(e){var t=this,n=this.callbackMap[e];return n||(n=this.callbackMap[e]=function(n){t.handleValue(n,String(e))}),n},e.prototype.collect=function(e,t,n){return Fe(this.currentMap,e,t,n)},e.prototype.getAll=function(){return Ue(this.currentMap)},e}();function Wi(e){for(var t=0,n=0,r=F(e,".fc-scrollgrid-shrink");n0&&(this.everMovedDown=!0),i<0?this.everMovedLeft=!0:i>0&&(this.everMovedRight=!0),this.pointerScreenX=n,this.pointerScreenY=r,this.isAnimating||(this.isAnimating=!0,this.requestAnimation(ba()))}},e.prototype.stop=function(){if(this.isEnabled){this.isAnimating=!1;for(var e=0,t=this.scrollCaches;e=0&&u>=0&&c>=0&&d>=0&&(c<=n&&this.everMovedUp&&a.canScrollUp()&&(!r||r.distance>c)&&(r={scrollCache:a,name:"top",distance:c}),d<=n&&this.everMovedDown&&a.canScrollDown()&&(!r||r.distance>d)&&(r={scrollCache:a,name:"bottom",distance:d}),l<=n&&this.everMovedLeft&&a.canScrollLeft()&&(!r||r.distance>l)&&(r={scrollCache:a,name:"left",distance:l}),u<=n&&this.everMovedRight&&a.canScrollRight()&&(!r||r.distance>u)&&(r={scrollCache:a,name:"right",distance:u}))}return r},e.prototype.buildCaches=function(){return this.queryScrollEls().map((function(e){return e===window?new Da(!1):new Sa(e,!1)}))},e.prototype.queryScrollEls=function(){for(var e=[],t=0,n=this.scrollQuery;t=t*t&&r.handleDistanceSurpassed(e)}r.isDragging&&("scroll"!==e.origEvent.type&&(r.mirror.handleMove(e.pageX,e.pageY),r.autoScroller.handleMove(e.pageX,e.pageY)),r.emitter.trigger("dragmove",e))}},r.onPointerUp=function(e){r.isInteracting&&(r.isInteracting=!1,ne(document.body),oe(document.body),r.emitter.trigger("pointerup",e),r.isDragging&&(r.autoScroller.stop(),r.tryStopDrag(e)),r.delayTimeoutId&&(clearTimeout(r.delayTimeoutId),r.delayTimeoutId=null))};var o=r.pointer=new ga(t);return o.emitter.on("pointerdown",r.onPointerDown),o.emitter.on("pointermove",r.onPointerMove),o.emitter.on("pointerup",r.onPointerUp),n&&(o.selector=n),r.mirror=new ya,r.autoScroller=new Ca,r}return n(t,e),t.prototype.destroy=function(){this.pointer.destroy(),this.onPointerUp({})},t.prototype.startDelay=function(e){var t=this;"number"==typeof this.delay?this.delayTimeoutId=setTimeout((function(){t.delayTimeoutId=null,t.handleDelayEnd(e)}),this.delay):this.handleDelayEnd(e)},t.prototype.handleDelayEnd=function(e){this.isDelayEnded=!0,this.tryStartDrag(e)},t.prototype.handleDistanceSurpassed=function(e){this.isDistanceSurpassed=!0,this.tryStartDrag(e)},t.prototype.tryStartDrag=function(e){this.isDelayEnded&&this.isDistanceSurpassed&&(this.pointer.wasTouchScroll&&!this.touchScrollAllowed||(this.isDragging=!0,this.mirrorNeedsRevert=!1,this.autoScroller.start(e.pageX,e.pageY),this.emitter.trigger("dragstart",e),!1===this.touchScrollAllowed&&this.pointer.cancelTouchScroll()))},t.prototype.tryStopDrag=function(e){this.mirror.stop(this.mirrorNeedsRevert,this.stopDrag.bind(this,e))},t.prototype.stopDrag=function(e){this.isDragging=!1,this.emitter.trigger("dragend",e)},t.prototype.setIgnoreMove=function(e){this.pointer.shouldIgnoreMove=e},t.prototype.setMirrorIsVisible=function(e){this.mirror.setIsVisible(e)},t.prototype.setMirrorNeedsRevert=function(e){this.mirrorNeedsRevert=e},t.prototype.setAutoScrollEnabled=function(e){this.autoScroller.isEnabled=e},t}(ci),Ra=function(){function e(e){this.origRect=wr(e),this.scrollCaches=Rr(e).map((function(e){return new Sa(e,!0)}))}return e.prototype.destroy=function(){for(var e=0,t=this.scrollCaches;e=0&&c=0&&do.layer)&&(v.rect.left+=l,v.rect.right+=l,v.rect.top+=u,v.rect.bottom+=u,o=v)}}}return o},e}();function ka(e,t){return!e&&!t||Boolean(e)===Boolean(t)&&Rn(e.dateSpan,t.dateSpan)}function Ma(e,t){for(var n,o,i={},a=0,s=t.pluginHooks.datePointTransforms;ao.start)return c.endDelta=u,c;return null}(s,e,o.subjectEl.classList.contains("fc-event-resizer-start"),l.range,i.pluginHooks.eventResizeJoinTransforms)),u&&(c=Pn(a,i.getCurrentData().eventUiBases,u,i),p.mutatedEvents=c,n.component.isInteractionValid(p)||(d=!0,u=null,c=null,p.mutatedEvents=null)),c?i.dispatch({type:"SET_EVENT_RESIZE",state:p}):i.dispatch({type:"UNSET_EVENT_RESIZE"}),d?Q():ee(),t||(u&&ka(s,e)&&(u=null),n.validMutation=u,n.mutatedRelevantEvents=c)},n.handleDragEnd=function(e){var t=n.component.context,o=n.eventRange.def,i=n.eventRange.instance,a=new zn(t,o,i),s=n.relevantEvents,l=n.mutatedRelevantEvents;if(t.emitter.trigger("eventResizeStop",{el:n.draggingSegEl,event:a,jsEvent:e.origEvent,view:t.viewApi}),n.validMutation){var u=new zn(t,l.defs[o.defId],i?l.instances[i.instanceId]:null);t.dispatch({type:"MERGE_EVENTS",eventStore:l});var c={oldEvent:a,event:u,relatedEvents:Bn(l,t,i),revert:function(){t.dispatch({type:"MERGE_EVENTS",eventStore:s})}};t.emitter.trigger("eventResize",r(r({},c),{el:n.draggingSegEl,startDelta:n.validMutation.startDelta||Ye(0),endDelta:n.validMutation.endDelta||Ye(0),jsEvent:e.origEvent,view:t.viewApi})),t.emitter.trigger("eventChange",c)}else t.emitter.trigger("_noEventResize");n.draggingSeg=null,n.relevantEvents=null,n.validMutation=null};var o=t.component,i=n.dragging=new wa(t.el);i.pointer.selector=".fc-event-resizer",i.touchScrollAllowed=!1,i.autoScroller.isEnabled=o.context.options.dragScroll;var a=n.hitDragging=new Ta(n.dragging,li(t));return a.emitter.on("pointerdown",n.handlePointerDown),a.emitter.on("dragstart",n.handleDragStart),a.emitter.on("hitupdate",n.handleHitUpdate),a.emitter.on("dragend",n.handleDragEnd),n}return n(t,e),t.prototype.destroy=function(){this.dragging.destroy()},t.prototype.querySegEl=function(e){return V(e.subjectEl,".fc-event")},t}(ai);var Na=function(){function e(e){var t=this;this.context=e,this.isRecentPointerDateSelect=!1,this.matchesCancel=!1,this.matchesEvent=!1,this.onSelect=function(e){e.jsEvent&&(t.isRecentPointerDateSelect=!0)},this.onDocumentPointerDown=function(e){var n=t.context.options.unselectCancel,r=e.origEvent.target;t.matchesCancel=!!V(r,n),t.matchesEvent=!!V(r,Ia.SELECTOR)},this.onDocumentPointerUp=function(e){var n=t.context,r=t.documentPointer,o=n.getCurrentData();if(!r.wasTouchScroll){if(o.dateSelection&&!t.isRecentPointerDateSelect){var i=n.options.unselectAuto;!i||i&&t.matchesCancel||n.calendarApi.unselect(e)}o.eventSelection&&!t.matchesEvent&&n.dispatch({type:"UNSELECT_EVENT"})}t.isRecentPointerDateSelect=!1};var n=this.documentPointer=new ga(document);n.shouldIgnoreMove=!0,n.shouldWatchScroll=!1,n.emitter.on("pointerdown",this.onDocumentPointerDown),n.emitter.on("pointerup",this.onDocumentPointerUp),e.emitter.on("select",this.onSelect)}return e.prototype.destroy=function(){this.context.emitter.off("select",this.onSelect),this.documentPointer.destroy()},e}(),Ha={dateClick:_t,eventDragStart:_t,eventDragStop:_t,eventDrop:_t,eventResizeStart:_t,eventResizeStop:_t,eventResize:_t,drop:_t,eventReceive:_t,eventLeave:_t},Oa=function(){function e(e,t){var n=this;this.receivingContext=null,this.droppableEvent=null,this.suppliedDragMeta=null,this.dragMeta=null,this.handleDragStart=function(e){n.dragMeta=n.buildDragMeta(e.subjectEl)},this.handleHitUpdate=function(e,t,o){var i=n.hitDragging.dragging,a=null,s=null,l=!1,u={affectedEvents:{defs:{},instances:{}},mutatedEvents:{defs:{},instances:{}},isEvent:n.dragMeta.create};e&&(a=e.component.context,n.canDropElOnCalendar(o.subjectEl,a)&&(s=function(e,t,n){for(var o=r({},t.leftoverProps),i=0,a=n.pluginHooks.externalDefTransforms;ia.top)return!1}return!0}(e,t,n)){for(var r=e.firstCol;r<=e.lastCol;r++){for(var o=u[r],i=0;i=o[i].top;)i++;o.splice(i,0,{seg:e,top:n,bottom:n+t})}return!0}return!1}for(var M in i)i[M]||(d[M.split(":")[0]]=!0);return{segsByFirstCol:u.map(ts),segsByEachCol:u.map((function(t,n){var o=function(e){for(var t=[],n=0,r=e;nc;)h(s[l].pop(),s[l].length,!1)}}}}var rs=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.cellElRefs=new Li,t.frameElRefs=new Li,t.fgElRefs=new Li,t.segHarnessRefs=new Li,t.rootElRef=Ar(),t.state={framePositions:null,maxContentHeight:null,segHeights:{}},t}return n(t,e),t.prototype.render=function(){var e=this,t=this.props,n=this.state,r=this.context,o=t.cells.length,i=Fa(t.businessHourSegs,o),a=Fa(t.bgEventSegs,o),s=Fa(this.getHighlightSegs(),o),l=Fa(this.getMirrorSegs(),o),u=es(t.cells,t.fgEventSegs,t.dayMaxEvents,t.dayMaxEventRows,n.segHeights,n.maxContentHeight,o,r.options.eventOrder),c=u.paddingBottoms,d=u.segsByFirstCol,p=u.segsByEachCol,f=u.segIsHidden,h=u.segTops,v=u.segMarginTops,g=u.moreCnts,m=u.moreTops,y=t.eventDrag&&t.eventDrag.affectedInstances||t.eventResize&&t.eventResize.affectedInstances||{};return Hr("tr",{ref:this.rootElRef},t.renderIntro&&t.renderIntro(),t.cells.map((function(n,r){var o=e.renderFgSegs(d[r],f,h,v,y,t.todayRange),u=e.renderFgSegs(l[r],{},h,{},{},t.todayRange,Boolean(t.eventDrag),Boolean(t.eventResize),!1);return Hr(Ga,{key:n.key,elRef:e.cellElRefs.createRef(n.key),innerElRef:e.frameElRefs.createRef(n.key),dateProfile:t.dateProfile,date:n.date,showDayNumber:t.showDayNumbers,showWeekNumber:t.showWeekNumbers&&0===r,forceDayTop:t.showWeekNumbers,todayRange:t.todayRange,extraHookProps:n.extraHookProps,extraDataAttrs:n.extraDataAttrs,extraClassNames:n.extraClassNames,moreCnt:g[r],buildMoreLinkText:t.buildMoreLinkText,onMoreClick:t.onMoreClick,segIsHidden:f,moreMarginTop:m[r],segsByEachCol:p[r],fgPaddingBottom:c[r],fgContentElRef:e.fgElRefs.createRef(n.key),fgContent:Hr(Ur,null,Hr(Ur,null,o),Hr(Ur,null,u)),bgContent:Hr(Ur,null,e.renderFillSegs(s[r],"highlight"),e.renderFillSegs(i[r],"non-business"),e.renderFillSegs(a[r],"bg-event"))})})))},t.prototype.componentDidMount=function(){this.updateSizing(!0)},t.prototype.componentDidUpdate=function(e,t){var n=this.props;this.updateSizing(!Le(e,n))},t.prototype.getHighlightSegs=function(){var e=this.props;return e.eventDrag&&e.eventDrag.segs.length?e.eventDrag.segs:e.eventResize&&e.eventResize.segs.length?e.eventResize.segs:e.dateSelectionSegs},t.prototype.getMirrorSegs=function(){var e=this.props;return e.eventResize&&e.eventResize.segs.length?e.eventResize.segs:[]},t.prototype.renderFgSegs=function(e,t,n,o,i,a,s,l,u){var c=this.context,d=this.props.eventSelection,p=this.state.framePositions,f=1===this.props.cells.length,h=[];if(p)for(var v=0,g=e;v1,showWeekNumbers:t.showWeekNumbers,todayRange:g,dateProfile:n,cells:i,renderIntro:t.renderRowIntro,businessHourSegs:l[a],eventSelection:t.eventSelection,bgEventSegs:u[a].filter(ls),fgEventSegs:c[a],dateSelectionSegs:d[a],eventDrag:p[a],eventResize:f[a],dayMaxEvents:o,dayMaxEventRows:r,clientWidth:t.clientWidth,clientHeight:t.clientHeight,buildMoreLinkText:h,onMoreClick:e.handleMoreLinkClick})})))),!t.forPrint&&a&&a.currentFgEventSegs===t.fgEventSegs&&Hr(is,{date:a.date,dateProfile:n,segs:a.allSegs,alignmentEl:a.dayEl,topAlignmentEl:1===s?t.headerAlignElRef.current:null,onCloseClick:e.handleMorePopoverClose,selectedInstanceId:t.eventSelection,hiddenInstances:(t.eventDrag?t.eventDrag.affectedInstances:null)||(t.eventResize?t.eventResize.affectedInstances:null)||{},todayRange:g}))})))},t.prototype.prepareHits=function(){this.rowPositions=new Mr(this.rootEl,this.rowRefs.collect().map((function(e){return e.getCellEls()[0]})),!1,!0),this.colPositions=new Mr(this.rootEl,this.rowRefs.currentMap[0].getCellEls(),!0,!1)},t.prototype.positionToHit=function(e,t){var n=this.colPositions,r=this.rowPositions,o=n.leftToIndex(e),i=r.topToIndex(t);if(null!=i&&null!=o)return{row:i,col:o,dateSpan:{range:this.getCellRange(i,o),allDay:!0},dayEl:this.getCellEl(i,o),relativeRect:{left:n.lefts[o],right:n.rights[o],top:r.tops[i],bottom:r.bottoms[i]}}},t.prototype.getCellEl=function(e,t){return this.rowRefs.currentMap[e].getCellEls()[t]},t.prototype.getCellRange=function(e,t){var n=this.props.cells[e][t].date;return{start:n,end:ve(n,1)}},t}(oo);function ss(e){return"function"==typeof e?e:function(t){return"+"+t+" "+e}}function ls(e){return e.eventRange.def.allDay}var us=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.slicer=new cs,t.tableRef=Ar(),t.handleRootEl=function(e){e?t.context.registerInteractiveComponent(t,{el:e}):t.context.unregisterInteractiveComponent(t)},t}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.context;return Hr(as,r({ref:this.tableRef,elRef:this.handleRootEl},this.slicer.sliceProps(e,e.dateProfile,e.nextDayThreshold,t,e.dayTableModel),{dateProfile:e.dateProfile,cells:e.dayTableModel.cells,colGroupNode:e.colGroupNode,tableMinWidth:e.tableMinWidth,renderRowIntro:e.renderRowIntro,dayMaxEvents:e.dayMaxEvents,dayMaxEventRows:e.dayMaxEventRows,showWeekNumbers:e.showWeekNumbers,expandRows:e.expandRows,headerAlignElRef:e.headerAlignElRef,clientWidth:e.clientWidth,clientHeight:e.clientHeight,forPrint:e.forPrint}))},t.prototype.prepareHits=function(){this.tableRef.current.prepareHits()},t.prototype.queryHit=function(e,t){var n=this.tableRef.current.positionToHit(e,t);if(n)return{component:this,dateSpan:n.dateSpan,dayEl:n.dayEl,rect:{left:n.relativeRect.left,right:n.relativeRect.right,top:n.relativeRect.top,bottom:n.relativeRect.bottom},layer:0}},t}(oo),cs=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.forceDayIfListItem=!0,t}return n(t,e),t.prototype.sliceRange=function(e,t){return t.sliceRange(e)},t}(Hi),ds=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.buildDayTableModel=it(ps),t.headerRef=Ar(),t.tableRef=Ar(),t}return n(t,e),t.prototype.render=function(){var e=this,t=this.context,n=t.options,r=t.dateProfileGenerator,o=this.props,i=this.buildDayTableModel(o.dateProfile,r),a=n.dayHeaders&&Hr(_i,{ref:this.headerRef,dateProfile:o.dateProfile,dates:i.headerDates,datesRepDistinctDays:1===i.rowCnt}),s=function(t){return Hr(us,{ref:e.tableRef,dateProfile:o.dateProfile,dayTableModel:i,businessHours:o.businessHours,dateSelection:o.dateSelection,eventStore:o.eventStore,eventUiBases:o.eventUiBases,eventSelection:o.eventSelection,eventDrag:o.eventDrag,eventResize:o.eventResize,nextDayThreshold:n.nextDayThreshold,colGroupNode:t.tableColGroupNode,tableMinWidth:t.tableMinWidth,dayMaxEvents:n.dayMaxEvents,dayMaxEventRows:n.dayMaxEventRows,showWeekNumbers:n.weekNumbers,expandRows:!o.isHeightAuto,headerAlignElRef:e.headerElRef,clientWidth:t.clientWidth,clientHeight:t.clientHeight,forPrint:o.forPrint})};return n.dayMinWidth?this.renderHScrollLayout(a,s,i.colCnt,n.dayMinWidth):this.renderSimpleLayout(a,s)},t}(Va);function ps(e,t){var n=new Pi(e.renderRange,t);return new Ni(n,/year|month|week/.test(e.currentRangeUnit))}var fs=io({initialView:"dayGridMonth",optionRefiners:{moreLinkClick:_t,moreLinkClassNames:_t,moreLinkContent:_t,moreLinkDidMount:_t,moreLinkWillUnmount:_t},views:{dayGrid:{component:ds,dateProfileGeneratorClass:function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.buildRenderRange=function(t,n,r){var o,i=this.props.dateEnv,a=e.prototype.buildRenderRange.call(this,t,n,r),s=a.start,l=a.end;(/^(year|month)$/.test(n)&&(s=i.startOfWeek(s),(o=i.startOfWeek(l)).valueOf()!==l.valueOf()&&(l=he(o,1))),this.props.monthMode&&this.props.fixedWeekCount)&&(l=he(l,6-Math.ceil(me(s,l))));return{start:s,end:l}},t}(Co)},dayGridDay:{type:"dayGrid",duration:{days:1}},dayGridWeek:{type:"dayGrid",duration:{weeks:1}},dayGridMonth:{type:"dayGrid",duration:{months:1},monthMode:!0,fixedWeekCount:!0}}}),hs=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.getKeyInfo=function(){return{allDay:{},timed:{}}},t.prototype.getKeysForDateSpan=function(e){return e.allDay?["allDay"]:["timed"]},t.prototype.getKeysForEventDef=function(e){return e.allDay?un(e)?["timed","allDay"]:["allDay"]:["timed"]},t}(pr),vs=function(){function e(e,t,n){this.positions=e,this.dateProfile=t,this.slatMetas=n}return e.prototype.safeComputeTop=function(e){var t=this.dateProfile;if(sn(t.currentRange,e)){var n=be(e),r=e.valueOf()-n.valueOf();if(r>=$e(t.slotMinTime)&&r<$e(t.slotMaxTime))return this.computeTimeTop(Ye(r))}},e.prototype.computeDateTop=function(e,t){return t||(t=be(e)),this.computeTimeTop(Ye(e.valueOf()-t.valueOf()))},e.prototype.computeTimeTop=function(e){var t,n,r=this.positions,o=this.dateProfile,i=this.slatMetas,a=r.els.length,s=i[1].date.valueOf()-i[0].date.valueOf(),l=(e.milliseconds-$e(o.slotMinTime))/s;return l=Math.max(0,l),l=Math.min(a,l),t=Math.floor(l),n=l-(t=Math.min(t,a-1)),r.tops[t]+r.getHeight(t)*n},e}(),gs=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}],ms=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.rootElRef=Ar(),t.slatElRefs=new Li,t}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.context;return Hr("div",{className:"fc-timegrid-slots",ref:this.rootElRef},Hr("table",{className:t.theme.getClass("table"),style:{minWidth:e.tableMinWidth,width:e.clientWidth,height:e.minHeight}},e.tableColGroupNode,Hr(ys,{slatElRefs:this.slatElRefs,axis:e.axis,slatMetas:e.slatMetas})))},t.prototype.componentDidMount=function(){this.updateSizing()},t.prototype.componentDidUpdate=function(){this.updateSizing()},t.prototype.componentWillUnmount=function(){this.props.onCoords&&this.props.onCoords(null)},t.prototype.updateSizing=function(){var e,t=this.props;t.onCoords&&null!==t.clientWidth&&(this.rootElRef.current.offsetHeight&&t.onCoords(new vs(new Mr(this.rootElRef.current,(e=this.slatElRefs.currentMap,t.slatMetas.map((function(t){return e[t.key]}))),!1,!0),this.props.dateProfile,t.slatMetas)))},t}(jr);var ys=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.render=function(){var e=this.props,t=this.context,n=t.options,o=e.slatElRefs;return Hr("tbody",null,e.slatMetas.map((function(i,a){var s={time:i.time,date:t.dateEnv.toDate(i.date),view:t.viewApi},l=["fc-timegrid-slot","fc-timegrid-slot-lane",i.isLabeled?"":"fc-timegrid-slot-minor"];return Hr("tr",{key:i.key,ref:o.createRef(i.key)},e.axis&&Hr(Ss,r({},i)),Hr(uo,{hookProps:s,classNames:n.slotLaneClassNames,content:n.slotLaneContent,didMount:n.slotLaneDidMount,willUnmount:n.slotLaneWillUnmount},(function(e,t,n,r){return Hr("td",{ref:e,className:l.concat(t).join(" "),"data-time":i.isoTimeStr},r)})))})))},t}(jr),Es=St({hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"short"});function Ss(e){var t=["fc-timegrid-slot","fc-timegrid-slot-label",e.isLabeled?"fc-scrollgrid-shrink":"fc-timegrid-slot-minor"];return Hr(zr.Consumer,null,(function(n){if(e.isLabeled){var r=n.dateEnv,o=n.options,i=n.viewApi,a=null==o.slotLabelFormat?Es:Array.isArray(o.slotLabelFormat)?St(o.slotLabelFormat[0]):St(o.slotLabelFormat),s={level:0,time:e.time,date:r.toDate(e.date),view:i,text:r.format(e.date,a)};return Hr(uo,{hookProps:s,classNames:o.slotLabelClassNames,content:o.slotLabelContent,defaultContent:Ds,didMount:o.slotLabelDidMount,willUnmount:o.slotLabelWillUnmount},(function(n,r,o,i){return Hr("td",{ref:n,className:t.concat(r).join(" "),"data-time":e.isoTimeStr},Hr("div",{className:"fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame"},Hr("div",{className:"fc-timegrid-slot-label-cushion fc-scrollgrid-shrink-cushion",ref:o},i)))}))}return Hr("td",{className:t.join(" "),"data-time":e.isoTimeStr})}))}function Ds(e){return e.text}function bs(e,t,n,r,o){for(var i=new Date(0),a=e,s=Ye(0),l=n||function(e){var t,n,r;for(t=gs.length-1;t>=0;t--)if(n=Ye(gs[t]),null!==(r=Qe(n,e))&&r>1)return n;return e}(r),u=[];$e(a)<$e(t);){var c=o.add(i,a),d=null!==Qe(s,l);u.push({date:c,time:a,key:c.toISOString(),isoTimeStr:nt(c),isLabeled:d}),a=Xe(a,r),s=Xe(s,r)}return u}var Cs=St({week:"short"}),ws=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.allDaySplitter=new hs,t.headerElRef=Ar(),t.rootElRef=Ar(),t.scrollerElRef=Ar(),t.state={slatCoords:null},t.handleScrollTopRequest=function(e){var n=t.scrollerElRef.current;n&&(n.scrollTop=e)},t.renderHeadAxis=function(e){void 0===e&&(e="");var n=t.context.options,o=t.props.dateProfile.renderRange,i=ye(o.start,o.end),a=n.navLinks&&1===i?{"data-navlink":gr(o.start,"week"),tabIndex:0}:{};return n.weekNumbers?Hr(ca,{date:o.start,defaultFormat:Cs},(function(t,n,o,i){return Hr("th",{ref:t,className:["fc-timegrid-axis","fc-scrollgrid-shrink"].concat(n).join(" ")},Hr("div",{className:"fc-timegrid-axis-frame fc-scrollgrid-shrink-frame fc-timegrid-axis-frame-liquid",style:{height:e}},Hr("a",r({ref:o,className:"fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner"},a),i)))})):Hr("th",{className:"fc-timegrid-axis"},Hr("div",{className:"fc-timegrid-axis-frame",style:{height:e}}))},t.renderTableRowAxis=function(e){var n=t.context,r=n.options,o=n.viewApi,i={text:r.allDayText,view:o};return Hr(uo,{hookProps:i,classNames:r.allDayClassNames,content:r.allDayContent,defaultContent:Rs,didMount:r.allDayDidMount,willUnmount:r.allDayWillUnmount},(function(t,n,r,o){return Hr("td",{ref:t,className:["fc-timegrid-axis","fc-scrollgrid-shrink"].concat(n).join(" ")},Hr("div",{className:"fc-timegrid-axis-frame fc-scrollgrid-shrink-frame"+(null==e?" fc-timegrid-axis-frame-liquid":""),style:{height:e}},Hr("span",{className:"fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner",ref:r},o)))}))},t.handleSlatCoords=function(e){t.setState({slatCoords:e})},t}return n(t,e),t.prototype.renderSimpleLayout=function(e,t,n){var r=this.context,o=this.props,i=[],a=Ki(r.options);return e&&i.push({type:"header",key:"header",isSticky:a,chunk:{elRef:this.headerElRef,tableClassName:"fc-col-header",rowContent:e}}),t&&(i.push({type:"body",key:"all-day",chunk:{content:t}}),i.push({type:"body",key:"all-day-divider",outerContent:Hr("tr",{className:"fc-scrollgrid-section"},Hr("td",{className:"fc-timegrid-divider "+r.theme.getClass("tableCellShaded")}))})),i.push({type:"body",key:"body",liquid:!0,expandRows:Boolean(r.options.expandRows),chunk:{scrollerElRef:this.scrollerElRef,content:n}}),Hr(yo,{viewSpec:r.viewSpec,elRef:this.rootElRef},(function(e,t){return Hr("div",{className:["fc-timegrid"].concat(t).join(" "),ref:e},Hr($i,{liquid:!o.isHeightAuto&&!o.forPrint,cols:[{width:"shrink"}],sections:i}))}))},t.prototype.renderHScrollLayout=function(e,t,n,r,o,i,a){var s=this,l=this.context.pluginHooks.scrollGridImpl;if(!l)throw new Error("No ScrollGrid implementation");var u=this.context,c=this.props,d=!c.forPrint&&Ki(u.options),p=!c.forPrint&&Ji(u.options),f=[];e&&f.push({type:"header",key:"header",isSticky:d,syncRowHeights:!0,chunks:[{key:"axis",rowContent:function(e){return Hr("tr",null,s.renderHeadAxis(e.rowSyncHeights[0]))}},{key:"cols",elRef:this.headerElRef,tableClassName:"fc-col-header",rowContent:e}]}),t&&(f.push({type:"body",key:"all-day",syncRowHeights:!0,chunks:[{key:"axis",rowContent:function(e){return Hr("tr",null,s.renderTableRowAxis(e.rowSyncHeights[0]))}},{key:"cols",content:t}]}),f.push({key:"all-day-divider",type:"body",outerContent:Hr("tr",{className:"fc-scrollgrid-section"},Hr("td",{colSpan:2,className:"fc-timegrid-divider "+u.theme.getClass("tableCellShaded")}))}));var h=u.options.nowIndicator;return f.push({type:"body",key:"body",liquid:!0,expandRows:Boolean(u.options.expandRows),chunks:[{key:"axis",content:function(e){return Hr("div",{className:"fc-timegrid-axis-chunk"},Hr("table",{style:{height:e.expandRows?e.clientHeight:""}},e.tableColGroupNode,Hr("tbody",null,Hr(Ts,{slatMetas:i}))),Hr("div",{className:"fc-timegrid-now-indicator-container"},Hr(Mi,{unit:h?"minute":"day"},(function(e){var t=h&&a&&a.safeComputeTop(e);return"number"==typeof t?Hr(na,{isAxis:!0,date:e},(function(e,n,r,o){return Hr("div",{ref:e,className:["fc-timegrid-now-indicator-arrow"].concat(n).join(" "),style:{top:t}},o)})):null}))))}},{key:"cols",scrollerElRef:this.scrollerElRef,content:n}]}),p&&f.push({key:"footer",type:"footer",isSticky:!0,chunks:[{key:"axis",content:Xi},{key:"cols",content:Xi}]}),Hr(yo,{viewSpec:u.viewSpec,elRef:this.rootElRef},(function(e,t){return Hr("div",{className:["fc-timegrid"].concat(t).join(" "),ref:e},Hr(l,{liquid:!c.isHeightAuto&&!c.forPrint,colGroups:[{width:"shrink",cols:[{width:"shrink"}]},{cols:[{span:r,minWidth:o}]}],sections:f}))}))},t.prototype.getAllDayMaxEventProps=function(){var e=this.context.options,t=e.dayMaxEvents,n=e.dayMaxEventRows;return!0!==t&&!0!==n||(t=void 0,n=5),{dayMaxEvents:t,dayMaxEventRows:n}},t}(oo);function Rs(e){return e.text}var Ts=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.render=function(){return this.props.slatMetas.map((function(e){return Hr("tr",{key:e.key},Hr(Ss,r({},e)))}))},t}(jr);function ks(e,t){var n,r=[];for(n=0;ni.top&&o.top0?" fc-timegrid-event-harness-inset":""),key:s,style:r({visibility:t[s]?"hidden":""},u)},Hr(As,r({seg:e,isDragging:n,isResizing:o,isDateSelecting:i,isSelected:s===l.eventSelection,isCondensed:e.bottom-e.top0?e.renderSegList(s,i):e.renderEmptyMessage()))}))},t.prototype.renderEmptyMessage=function(){var e=this.context,t=e.options,n=e.viewApi,r={text:t.noEventsText,view:n};return Hr(uo,{hookProps:r,classNames:t.noEventsClassNames,content:t.noEventsContent,defaultContent:tl,didMount:t.noEventsDidMount,willUnmount:t.noEventsWillUnmount},(function(e,t,n,r){return Hr("div",{className:["fc-list-empty"].concat(t).join(" "),ref:e},Hr("div",{className:"fc-list-empty-cushion",ref:n},r))}))},t.prototype.renderSegList=function(e,t){var n=this.context,o=n.theme,i=n.options,a=function(e){var t,n,r=[];for(t=0;t + + + + + + + image/svg+xml + + + + + + + +   + + CLOUD + pen + + diff --git a/static/img/mic.png b/static/img/mic.png new file mode 100644 index 0000000..d4bc5d6 Binary files /dev/null and b/static/img/mic.png differ diff --git a/static/js/bootstrap.bundle.min.js b/static/js/bootstrap.bundle.min.js new file mode 100644 index 0000000..c682e1b --- /dev/null +++ b/static/js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.0.0-beta2 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";function t(t,e){for(var n=0;n0,i._pointerEvent=Boolean(window.PointerEvent),i._addEventListeners(),i}i(o,t);var r=o.prototype;return r.next=function(){this._isSliding||this._slide("next")},r.nextWhenVisible=function(){!document.hidden&&g(this._element)&&this.next()},r.prev=function(){this._isSliding||this._slide("prev")},r.pause=function(t){t||(this._isPaused=!0),Y(".carousel-item-next, .carousel-item-prev",this._element)&&(f(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},r.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},r.to=function(t){var e=this;this._activeElement=Y(".active.carousel-item",this._element);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)B.one(this._element,"slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?"next":"prev";this._slide(i,this._items[t])}},r.dispose=function(){t.prototype.dispose.call(this),B.off(this._element,".bs.carousel"),this._items=null,this._config=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},r._getConfig=function(t){return t=n({},X,t),p("carousel",t,Q),t},r._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&(b?this.next():this.prev()),e<0&&(b?this.prev():this.next())}},r._addEventListeners=function(){var t=this;this._config.keyboard&&B.on(this._element,"keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&(B.on(this._element,"mouseenter.bs.carousel",(function(e){return t.pause(e)})),B.on(this._element,"mouseleave.bs.carousel",(function(e){return t.cycle(e)}))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()},r._addTouchEventListeners=function(){var t=this,e=function(e){!t._pointerEvent||"pen"!==e.pointerType&&"touch"!==e.pointerType?t._pointerEvent||(t.touchStartX=e.touches[0].clientX):t.touchStartX=e.clientX},n=function(e){!t._pointerEvent||"pen"!==e.pointerType&&"touch"!==e.pointerType||(t.touchDeltaX=e.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};F(".carousel-item img",this._element).forEach((function(t){B.on(t,"dragstart.bs.carousel",(function(t){return t.preventDefault()}))})),this._pointerEvent?(B.on(this._element,"pointerdown.bs.carousel",(function(t){return e(t)})),B.on(this._element,"pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(B.on(this._element,"touchstart.bs.carousel",(function(t){return e(t)})),B.on(this._element,"touchmove.bs.carousel",(function(e){return function(e){e.touches&&e.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.touches[0].clientX-t.touchStartX}(e)})),B.on(this._element,"touchend.bs.carousel",(function(t){return n(t)})))},r._keydown=function(t){/input|textarea/i.test(t.target.tagName)||("ArrowLeft"===t.key?(t.preventDefault(),b?this.next():this.prev()):"ArrowRight"===t.key&&(t.preventDefault(),b?this.prev():this.next()))},r._getItemIndex=function(t){return this._items=t&&t.parentNode?F(".carousel-item",t.parentNode):[],this._items.indexOf(t)},r._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var s=(o+("prev"===t?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},r._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(Y(".active.carousel-item",this._element));return B.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n})},r._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=Y(".active",this._indicatorsElement);e.classList.remove("active"),e.removeAttribute("aria-current");for(var n=F("[data-bs-target]",this._indicatorsElement),i=0;i0)for(var i=0;i=0}function _t(t){return((ut(t)?t.ownerDocument:t.document)||window.document).documentElement}function bt(t){return"html"===lt(t)?t:t.assignedSlot||t.parentNode||t.host||_t(t)}function yt(t){if(!ft(t)||"fixed"===mt(t).position)return null;var e=t.offsetParent;if(e){var n=_t(e);if("body"===lt(e)&&"static"===mt(e).position&&"static"!==mt(n).position)return n}return e}function wt(t){for(var e=ct(t),n=yt(t);n&&vt(n)&&"static"===mt(n).position;)n=yt(n);return n&&"body"===lt(n)&&"static"===mt(n).position?e:n||function(t){for(var e=bt(t);ft(e)&&["html","body"].indexOf(lt(e))<0;){var n=mt(e);if("none"!==n.transform||"none"!==n.perspective||n.willChange&&"auto"!==n.willChange)return e;e=e.parentNode}return null}(t)||e}function Et(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Tt(t,e,n){return Math.max(t,Math.min(e,n))}function kt(t){return Object.assign(Object.assign({},{top:0,right:0,bottom:0,left:0}),t)}function At(t,e){return e.reduce((function(e,n){return e[n]=t,e}),{})}var Lt={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,n=t.state,i=t.name,o=n.elements.arrow,r=n.modifiersData.popperOffsets,s=ht(n.placement),a=Et(s),l=[it,nt].indexOf(s)>=0?"height":"width";if(o&&r){var c=n.modifiersData[i+"#persistent"].padding,u=pt(o),f="y"===a?tt:it,d="y"===a?et:nt,h=n.rects.reference[l]+n.rects.reference[a]-r[a]-n.rects.popper[l],p=r[a]-n.rects.reference[a],g=wt(o),m=g?"y"===a?g.clientHeight||0:g.clientWidth||0:0,v=h/2-p/2,_=c[f],b=m-u[l]-c[d],y=m/2-u[l]/2+v,w=Tt(_,y,b),E=a;n.modifiersData[i]=((e={})[E]=w,e.centerOffset=w-y,e)}},effect:function(t){var e=t.state,n=t.options,i=t.name,o=n.element,r=void 0===o?"[data-popper-arrow]":o,s=n.padding,a=void 0===s?0:s;null!=r&&("string"!=typeof r||(r=e.elements.popper.querySelector(r)))&>(e.elements.popper,r)&&(e.elements.arrow=r,e.modifiersData[i+"#persistent"]={padding:kt("number"!=typeof a?a:At(a,ot))})},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},Ot={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Dt(t){var e,n=t.popper,i=t.popperRect,o=t.placement,r=t.offsets,s=t.position,a=t.gpuAcceleration,l=t.adaptive,c=t.roundOffsets?function(t){var e=t.x,n=t.y,i=window.devicePixelRatio||1;return{x:Math.round(e*i)/i||0,y:Math.round(n*i)/i||0}}(r):r,u=c.x,f=void 0===u?0:u,d=c.y,h=void 0===d?0:d,p=r.hasOwnProperty("x"),g=r.hasOwnProperty("y"),m=it,v=tt,_=window;if(l){var b=wt(n);b===ct(n)&&(b=_t(n)),o===tt&&(v=et,h-=b.clientHeight-i.height,h*=a?1:-1),o===it&&(m=nt,f-=b.clientWidth-i.width,f*=a?1:-1)}var y,w=Object.assign({position:s},l&&Ot);return a?Object.assign(Object.assign({},w),{},((y={})[v]=g?"0":"",y[m]=p?"0":"",y.transform=(_.devicePixelRatio||1)<2?"translate("+f+"px, "+h+"px)":"translate3d("+f+"px, "+h+"px, 0)",y)):Object.assign(Object.assign({},w),{},((e={})[v]=g?h+"px":"",e[m]=p?f+"px":"",e.transform="",e))}var xt={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,n=t.options,i=n.gpuAcceleration,o=void 0===i||i,r=n.adaptive,s=void 0===r||r,a=n.roundOffsets,l=void 0===a||a,c={placement:ht(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:o};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign(Object.assign({},e.styles.popper),Dt(Object.assign(Object.assign({},c),{},{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:s,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign(Object.assign({},e.styles.arrow),Dt(Object.assign(Object.assign({},c),{},{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign(Object.assign({},e.attributes.popper),{},{"data-popper-placement":e.placement})},data:{}},Ct={passive:!0},St={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,n=t.instance,i=t.options,o=i.scroll,r=void 0===o||o,s=i.resize,a=void 0===s||s,l=ct(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return r&&c.forEach((function(t){t.addEventListener("scroll",n.update,Ct)})),a&&l.addEventListener("resize",n.update,Ct),function(){r&&c.forEach((function(t){t.removeEventListener("scroll",n.update,Ct)})),a&&l.removeEventListener("resize",n.update,Ct)}},data:{}},jt={left:"right",right:"left",bottom:"top",top:"bottom"};function Nt(t){return t.replace(/left|right|bottom|top/g,(function(t){return jt[t]}))}var Pt={start:"end",end:"start"};function It(t){return t.replace(/start|end/g,(function(t){return Pt[t]}))}function Mt(t){var e=t.getBoundingClientRect();return{width:e.width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function Bt(t){var e=ct(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ht(t){return Mt(_t(t)).left+Bt(t).scrollLeft}function Rt(t){var e=mt(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+i)}function Wt(t,e){void 0===e&&(e=[]);var n=function t(e){return["html","body","#document"].indexOf(lt(e))>=0?e.ownerDocument.body:ft(e)&&Rt(e)?e:t(bt(e))}(t),i="body"===lt(n),o=ct(n),r=i?[o].concat(o.visualViewport||[],Rt(n)?n:[]):n,s=e.concat(r);return i?s:s.concat(Wt(bt(r)))}function Kt(t){return Object.assign(Object.assign({},t),{},{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Ut(t,e){return"viewport"===e?Kt(function(t){var e=ct(t),n=_t(t),i=e.visualViewport,o=n.clientWidth,r=n.clientHeight,s=0,a=0;return i&&(o=i.width,r=i.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(s=i.offsetLeft,a=i.offsetTop)),{width:o,height:r,x:s+Ht(t),y:a}}(t)):ft(e)?function(t){var e=Mt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Kt(function(t){var e=_t(t),n=Bt(t),i=t.ownerDocument.body,o=Math.max(e.scrollWidth,e.clientWidth,i?i.scrollWidth:0,i?i.clientWidth:0),r=Math.max(e.scrollHeight,e.clientHeight,i?i.scrollHeight:0,i?i.clientHeight:0),s=-n.scrollLeft+Ht(t),a=-n.scrollTop;return"rtl"===mt(i||e).direction&&(s+=Math.max(e.clientWidth,i?i.clientWidth:0)-o),{width:o,height:r,x:s,y:a}}(_t(t)))}function zt(t){return t.split("-")[1]}function Ft(t){var e,n=t.reference,i=t.element,o=t.placement,r=o?ht(o):null,s=o?zt(o):null,a=n.x+n.width/2-i.width/2,l=n.y+n.height/2-i.height/2;switch(r){case tt:e={x:a,y:n.y-i.height};break;case et:e={x:a,y:n.y+n.height};break;case nt:e={x:n.x+n.width,y:l};break;case it:e={x:n.x-i.width,y:l};break;default:e={x:n.x,y:n.y}}var c=r?Et(r):null;if(null!=c){var u="y"===c?"height":"width";switch(s){case"start":e[c]=e[c]-(n[u]/2-i[u]/2);break;case"end":e[c]=e[c]+(n[u]/2-i[u]/2)}}return e}function Yt(t,e){void 0===e&&(e={});var n=e,i=n.placement,o=void 0===i?t.placement:i,r=n.boundary,s=void 0===r?"clippingParents":r,a=n.rootBoundary,l=void 0===a?"viewport":a,c=n.elementContext,u=void 0===c?"popper":c,f=n.altBoundary,d=void 0!==f&&f,h=n.padding,p=void 0===h?0:h,g=kt("number"!=typeof p?p:At(p,ot)),m="popper"===u?"reference":"popper",v=t.elements.reference,_=t.rects.popper,b=t.elements[d?m:u],y=function(t,e,n){var i="clippingParents"===e?function(t){var e=Wt(bt(t)),n=["absolute","fixed"].indexOf(mt(t).position)>=0&&ft(t)?wt(t):t;return ut(n)?e.filter((function(t){return ut(t)&>(t,n)&&"body"!==lt(t)})):[]}(t):[].concat(e),o=[].concat(i,[n]),r=o[0],s=o.reduce((function(e,n){var i=Ut(t,n);return e.top=Math.max(i.top,e.top),e.right=Math.min(i.right,e.right),e.bottom=Math.min(i.bottom,e.bottom),e.left=Math.max(i.left,e.left),e}),Ut(t,r));return s.width=s.right-s.left,s.height=s.bottom-s.top,s.x=s.left,s.y=s.top,s}(ut(b)?b:b.contextElement||_t(t.elements.popper),s,l),w=Mt(v),E=Ft({reference:w,element:_,strategy:"absolute",placement:o}),T=Kt(Object.assign(Object.assign({},_),E)),k="popper"===u?T:w,A={top:y.top-k.top+g.top,bottom:k.bottom-y.bottom+g.bottom,left:y.left-k.left+g.left,right:k.right-y.right+g.right},L=t.modifiersData.offset;if("popper"===u&&L){var O=L[o];Object.keys(A).forEach((function(t){var e=[nt,et].indexOf(t)>=0?1:-1,n=[tt,et].indexOf(t)>=0?"y":"x";A[t]+=O[n]*e}))}return A}function qt(t,e){void 0===e&&(e={});var n=e,i=n.placement,o=n.boundary,r=n.rootBoundary,s=n.padding,a=n.flipVariations,l=n.allowedAutoPlacements,c=void 0===l?st:l,u=zt(i),f=u?a?rt:rt.filter((function(t){return zt(t)===u})):ot,d=f.filter((function(t){return c.indexOf(t)>=0}));0===d.length&&(d=f);var h=d.reduce((function(e,n){return e[n]=Yt(t,{placement:n,boundary:o,rootBoundary:r,padding:s})[ht(n)],e}),{});return Object.keys(h).sort((function(t,e){return h[t]-h[e]}))}var Vt={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,n=t.options,i=t.name;if(!e.modifiersData[i]._skip){for(var o=n.mainAxis,r=void 0===o||o,s=n.altAxis,a=void 0===s||s,l=n.fallbackPlacements,c=n.padding,u=n.boundary,f=n.rootBoundary,d=n.altBoundary,h=n.flipVariations,p=void 0===h||h,g=n.allowedAutoPlacements,m=e.options.placement,v=ht(m),_=l||(v!==m&&p?function(t){if("auto"===ht(t))return[];var e=Nt(t);return[It(t),e,It(e)]}(m):[Nt(m)]),b=[m].concat(_).reduce((function(t,n){return t.concat("auto"===ht(n)?qt(e,{placement:n,boundary:u,rootBoundary:f,padding:c,flipVariations:p,allowedAutoPlacements:g}):n)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,T=!0,k=b[0],A=0;A=0,C=x?"width":"height",S=Yt(e,{placement:L,boundary:u,rootBoundary:f,altBoundary:d,padding:c}),j=x?D?nt:it:D?et:tt;y[C]>w[C]&&(j=Nt(j));var N=Nt(j),P=[];if(r&&P.push(S[O]<=0),a&&P.push(S[j]<=0,S[N]<=0),P.every((function(t){return t}))){k=L,T=!1;break}E.set(L,P)}if(T)for(var I=function(t){var e=b.find((function(e){var n=E.get(e);if(n)return n.slice(0,t).every((function(t){return t}))}));if(e)return k=e,"break"},M=p?3:1;M>0&&"break"!==I(M);M--);e.placement!==k&&(e.modifiersData[i]._skip=!0,e.placement=k,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function Xt(t,e,n){return void 0===n&&(n={x:0,y:0}),{top:t.top-e.height-n.y,right:t.right-e.width+n.x,bottom:t.bottom-e.height+n.y,left:t.left-e.width-n.x}}function Qt(t){return[tt,nt,et,it].some((function(e){return t[e]>=0}))}var $t={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,n=t.name,i=e.rects.reference,o=e.rects.popper,r=e.modifiersData.preventOverflow,s=Yt(e,{elementContext:"reference"}),a=Yt(e,{altBoundary:!0}),l=Xt(s,i),c=Xt(a,o,r),u=Qt(l),f=Qt(c);e.modifiersData[n]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:u,hasPopperEscaped:f},e.attributes.popper=Object.assign(Object.assign({},e.attributes.popper),{},{"data-popper-reference-hidden":u,"data-popper-escaped":f})}},Gt={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,n=t.options,i=t.name,o=n.offset,r=void 0===o?[0,0]:o,s=st.reduce((function(t,n){return t[n]=function(t,e,n){var i=ht(t),o=[it,tt].indexOf(i)>=0?-1:1,r="function"==typeof n?n(Object.assign(Object.assign({},e),{},{placement:t})):n,s=r[0],a=r[1];return s=s||0,a=(a||0)*o,[it,nt].indexOf(i)>=0?{x:a,y:s}:{x:s,y:a}}(n,e.rects,r),t}),{}),a=s[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[i]=s}},Zt={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,n=t.name;e.modifiersData[n]=Ft({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Jt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,n=t.options,i=t.name,o=n.mainAxis,r=void 0===o||o,s=n.altAxis,a=void 0!==s&&s,l=n.boundary,c=n.rootBoundary,u=n.altBoundary,f=n.padding,d=n.tether,h=void 0===d||d,p=n.tetherOffset,g=void 0===p?0:p,m=Yt(e,{boundary:l,rootBoundary:c,padding:f,altBoundary:u}),v=ht(e.placement),_=zt(e.placement),b=!_,y=Et(v),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,T=e.rects.reference,k=e.rects.popper,A="function"==typeof g?g(Object.assign(Object.assign({},e.rects),{},{placement:e.placement})):g,L={x:0,y:0};if(E){if(r){var O="y"===y?tt:it,D="y"===y?et:nt,x="y"===y?"height":"width",C=E[y],S=E[y]+m[O],j=E[y]-m[D],N=h?-k[x]/2:0,P="start"===_?T[x]:k[x],I="start"===_?-k[x]:-T[x],M=e.elements.arrow,B=h&&M?pt(M):{width:0,height:0},H=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=H[O],W=H[D],K=Tt(0,T[x],B[x]),U=b?T[x]/2-N-K-R-A:P-K-R-A,z=b?-T[x]/2+N+K+W+A:I+K+W+A,F=e.elements.arrow&&wt(e.elements.arrow),Y=F?"y"===y?F.clientTop||0:F.clientLeft||0:0,q=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,V=E[y]+U-q-Y,X=E[y]+z-q,Q=Tt(h?Math.min(S,V):S,C,h?Math.max(j,X):j);E[y]=Q,L[y]=Q-C}if(a){var $="x"===y?tt:it,G="x"===y?et:nt,Z=E[w],J=Tt(Z+m[$],Z,Z-m[G]);E[w]=J,L[w]=J-Z}e.modifiersData[i]=L}},requiresIfExists:["offset"]};function te(t,e,n){void 0===n&&(n=!1);var i,o,r=_t(e),s=Mt(t),a=ft(e),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(a||!a&&!n)&&(("body"!==lt(e)||Rt(r))&&(l=(i=e)!==ct(i)&&ft(i)?{scrollLeft:(o=i).scrollLeft,scrollTop:o.scrollTop}:Bt(i)),ft(e)?((c=Mt(e)).x+=e.clientLeft,c.y+=e.clientTop):r&&(c.x=Ht(r))),{x:s.left+l.scrollLeft-c.x,y:s.top+l.scrollTop-c.y,width:s.width,height:s.height}}var ee={placement:"bottom",modifiers:[],strategy:"absolute"};function ne(){for(var t=arguments.length,e=new Array(t),n=0;n0&&r--,"ArrowDown"===t.key&&rdocument.documentElement.clientHeight;e||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var n=u(this._dialog);B.off(this._element,"transitionend"),B.one(this._element,"transitionend",(function(){t._element.classList.remove("modal-static"),e||(B.one(t._element,"transitionend",(function(){t._element.style.overflowY=""})),h(t._element,n))})),h(this._element,n),this._element.focus()}},r._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;(!this._isBodyOverflowing&&t&&!b||this._isBodyOverflowing&&!t&&b)&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),(this._isBodyOverflowing&&!t&&!b||!this._isBodyOverflowing&&t&&b)&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},r._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},r._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},Ce={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},Se=function(t){function o(e,n){var i;if(void 0===ae)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");return(i=t.call(this,e)||this)._isEnabled=!0,i._timeout=0,i._hoverState="",i._activeTrigger={},i._popper=null,i.config=i._getConfig(n),i.tip=null,i._setListeners(),i}i(o,t);var r=o.prototype;return r.enable=function(){this._isEnabled=!0},r.disable=function(){this._isEnabled=!1},r.toggleEnabled=function(){this._isEnabled=!this._isEnabled},r.toggle=function(t){if(this._isEnabled)if(t){var e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains("show"))return void this._leave(null,this);this._enter(null,this)}},r.dispose=function(){clearTimeout(this._timeout),B.off(this._element,this.constructor.EVENT_KEY),B.off(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this.tip&&this.tip.parentNode&&this.tip.parentNode.removeChild(this.tip),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.config=null,this.tip=null,t.prototype.dispose.call(this)},r.show=function(){var t=this;if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(this.isWithContent()&&this._isEnabled){var e=B.trigger(this._element,this.constructor.Event.SHOW),n=function t(e){if(!document.documentElement.attachShadow)return null;if("function"==typeof e.getRootNode){var n=e.getRootNode();return n instanceof ShadowRoot?n:null}return e instanceof ShadowRoot?e:e.parentNode?t(e.parentNode):null}(this._element),i=null===n?this._element.ownerDocument.documentElement.contains(this._element):n.contains(this._element);if(!e.defaultPrevented&&i){var o=this.getTipElement(),r=s(this.constructor.NAME);o.setAttribute("id",r),this._element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&o.classList.add("fade");var a="function"==typeof this.config.placement?this.config.placement.call(this,o,this._element):this.config.placement,l=this._getAttachment(a);this._addAttachmentClass(l);var c=this._getContainer();E(o,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||c.appendChild(o),B.trigger(this._element,this.constructor.Event.INSERTED),this._popper=se(this._element,o,this._getPopperConfig(l)),o.classList.add("show");var f,d,p="function"==typeof this.config.customClass?this.config.customClass():this.config.customClass;p&&(f=o.classList).add.apply(f,p.split(" ")),"ontouchstart"in document.documentElement&&(d=[]).concat.apply(d,document.body.children).forEach((function(t){B.on(t,"mouseover",(function(){}))}));var g=function(){var e=t._hoverState;t._hoverState=null,B.trigger(t._element,t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(this.tip.classList.contains("fade")){var m=u(this.tip);B.one(this.tip,"transitionend",g),h(this.tip,m)}else g()}}},r.hide=function(){var t=this;if(this._popper){var e=this.getTipElement(),n=function(){"show"!==t._hoverState&&e.parentNode&&e.parentNode.removeChild(e),t._cleanTipClass(),t._element.removeAttribute("aria-describedby"),B.trigger(t._element,t.constructor.Event.HIDDEN),t._popper&&(t._popper.destroy(),t._popper=null)};if(!B.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented){var i;if(e.classList.remove("show"),"ontouchstart"in document.documentElement&&(i=[]).concat.apply(i,document.body.children).forEach((function(t){return B.off(t,"mouseover",m)})),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this.tip.classList.contains("fade")){var o=u(e);B.one(e,"transitionend",n),h(e,o)}else n();this._hoverState=""}}},r.update=function(){null!==this._popper&&this._popper.update()},r.isWithContent=function(){return Boolean(this.getTitle())},r.getTipElement=function(){if(this.tip)return this.tip;var t=document.createElement("div");return t.innerHTML=this.config.template,this.tip=t.children[0],this.tip},r.setContent=function(){var t=this.getTipElement();this.setElementContent(Y(".tooltip-inner",t),this.getTitle()),t.classList.remove("fade","show")},r.setElementContent=function(t,e){if(null!==t)return"object"==typeof e&&d(e)?(e.jquery&&(e=e[0]),void(this.config.html?e.parentNode!==t&&(t.innerHTML="",t.appendChild(e)):t.textContent=e.textContent)):void(this.config.html?(this.config.sanitize&&(e=ke(e,this.config.allowList,this.config.sanitizeFn)),t.innerHTML=e):t.textContent=e)},r.getTitle=function(){var t=this._element.getAttribute("data-bs-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this._element):this.config.title),t},r.updateAttachment=function(t){return"right"===t?"end":"left"===t?"start":t},r._initializeOnDelegatedTarget=function(t,e){var n=this.constructor.DATA_KEY;return(e=e||T(t.delegateTarget,n))||(e=new this.constructor(t.delegateTarget,this._getDelegateConfig()),E(t.delegateTarget,n,e)),e},r._getOffset=function(){var t=this,e=this.config.offset;return"string"==typeof e?e.split(",").map((function(t){return Number.parseInt(t,10)})):"function"==typeof e?function(n){return e(n,t._element)}:e},r._getPopperConfig=function(t){var e=this,i={placement:t,modifiers:[{name:"flip",options:{altBoundary:!0,fallbackPlacements:this.config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this.config.boundary}},{name:"arrow",options:{element:"."+this.constructor.NAME+"-arrow"}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:function(t){return e._handlePopperPlacementChange(t)}}],onFirstUpdate:function(t){t.options.placement!==t.placement&&e._handlePopperPlacementChange(t)}};return n({},i,"function"==typeof this.config.popperConfig?this.config.popperConfig(i):this.config.popperConfig)},r._addAttachmentClass=function(t){this.getTipElement().classList.add("bs-tooltip-"+this.updateAttachment(t))},r._getContainer=function(){return!1===this.config.container?document.body:d(this.config.container)?this.config.container:Y(this.config.container)},r._getAttachment=function(t){return De[t.toUpperCase()]},r._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)B.on(t._element,t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;B.on(t._element,n,t.config.selector,(function(e){return t._enter(e)})),B.on(t._element,i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t._element&&t.hide()},B.on(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=n({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},r._fixTitle=function(){var t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))},r._enter=function(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e.getTipElement().classList.contains("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},r._leave=function(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},r._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},r._getConfig=function(t){var e=z.getDataAttributes(this._element);return Object.keys(e).forEach((function(t){Le.has(t)&&delete e[t]})),t&&"object"==typeof t.container&&t.container.jquery&&(t.container=t.container[0]),"number"==typeof(t=n({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),p("tooltip",t,this.constructor.DefaultType),t.sanitize&&(t.template=ke(t.template,t.allowList,t.sanitizeFn)),t},r._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},r._cleanTipClass=function(){var t=this.getTipElement(),e=t.getAttribute("class").match(Ae);null!==e&&e.length>0&&e.map((function(t){return t.trim()})).forEach((function(e){return t.classList.remove(e)}))},r._handlePopperPlacementChange=function(t){var e=t.state;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))},o.jQueryInterface=function(t){return this.each((function(){var e=T(this,"bs.tooltip"),n="object"==typeof t&&t;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n)),"string"==typeof t)){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},e(o,null,[{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return"tooltip"}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Ce}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Oe}}]),o}(H);y("tooltip",Se);var je=new RegExp("(^|\\s)bs-popover\\S+","g"),Ne=n({},Se.Default,{placement:"right",offset:[0,8],trigger:"click",content:"",template:''}),Pe=n({},Se.DefaultType,{content:"(string|element|function)"}),Ie={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Me=function(t){function n(){return t.apply(this,arguments)||this}i(n,t);var o=n.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.setContent=function(){var t=this.getTipElement();this.setElementContent(Y(".popover-header",t),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this._element)),this.setElementContent(Y(".popover-body",t),e),t.classList.remove("fade","show")},o._addAttachmentClass=function(t){this.getTipElement().classList.add("bs-popover-"+this.updateAttachment(t))},o._getContent=function(){return this._element.getAttribute("data-bs-content")||this.config.content},o._cleanTipClass=function(){var t=this.getTipElement(),e=t.getAttribute("class").match(je);null!==e&&e.length>0&&e.map((function(t){return t.trim()})).forEach((function(e){return t.classList.remove(e)}))},n.jQueryInterface=function(t){return this.each((function(){var e=T(this,"bs.popover"),i="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new n(this,i),E(this,"bs.popover",e)),"string"==typeof t)){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},e(n,null,[{key:"Default",get:function(){return Ne}},{key:"NAME",get:function(){return"popover"}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return Ie}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Pe}}]),n}(Se);y("popover",Me);var Be={offset:10,method:"auto",target:""},He={offset:"number",method:"string",target:"(string|element)"},Re=function(t){function o(e,n){var i;return(i=t.call(this,e)||this)._scrollElement="BODY"===e.tagName?window:e,i._config=i._getConfig(n),i._selector=i._config.target+" .nav-link, "+i._config.target+" .list-group-item, "+i._config.target+" .dropdown-item",i._offsets=[],i._targets=[],i._activeTarget=null,i._scrollHeight=0,B.on(i._scrollElement,"scroll.bs.scrollspy",(function(){return i._process()})),i.refresh(),i._process(),i}i(o,t);var r=o.prototype;return r.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,i="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),F(this._selector).map((function(t){var e=l(t),o=e?Y(e):null;if(o){var r=o.getBoundingClientRect();if(r.width||r.height)return[z[n](o).top+i,e]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},r.dispose=function(){t.prototype.dispose.call(this),B.off(this._scrollElement,".bs.scrollspy"),this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},r._getConfig=function(t){if("string"!=typeof(t=n({},Be,"object"==typeof t&&t?t:{})).target&&d(t.target)){var e=t.target.id;e||(e=s("scrollspy"),t.target.id=e),t.target="#"+e}return p("scrollspy",t,He),t},r._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},r._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},r._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},r._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&(void 0===this._offsets[o+1]||t li > .active":".active";e=(e=F(o,i))[e.length-1]}var r=e?B.trigger(e,"hide.bs.tab",{relatedTarget:this._element}):null;if(!(B.trigger(this._element,"show.bs.tab",{relatedTarget:e}).defaultPrevented||null!==r&&r.defaultPrevented)){this._activate(this._element,i);var s=function(){B.trigger(e,"hidden.bs.tab",{relatedTarget:t._element}),B.trigger(t._element,"shown.bs.tab",{relatedTarget:e})};n?this._activate(n,n.parentNode,s):s()}}},o._activate=function(t,e,n){var i=this,o=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?q(e,".active"):F(":scope > li > .active",e))[0],r=n&&o&&o.classList.contains("fade"),s=function(){return i._transitionComplete(t,o,n)};if(o&&r){var a=u(o);o.classList.remove("show"),B.one(o,"transitionend",s),h(o,a)}else s()},o._transitionComplete=function(t,e,n){if(e){e.classList.remove("active");var i=Y(":scope > .dropdown-menu .active",e.parentNode);i&&i.classList.remove("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),v(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&t.parentNode.classList.contains("dropdown-menu")&&(t.closest(".dropdown")&&F(".dropdown-toggle").forEach((function(t){return t.classList.add("active")})),t.setAttribute("aria-expanded",!0)),n&&n()},n.jQueryInterface=function(t){return this.each((function(){var e=T(this,"bs.tab")||new n(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},e(n,null,[{key:"DATA_KEY",get:function(){return"bs.tab"}}]),n}(H);B.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){t.preventDefault(),(T(this,"bs.tab")||new We(this)).show()})),y("tab",We);var Ke={animation:"boolean",autohide:"boolean",delay:"number"},Ue={animation:!0,autohide:!0,delay:5e3},ze=function(t){function o(e,n){var i;return(i=t.call(this,e)||this)._config=i._getConfig(n),i._timeout=null,i._setListeners(),i}i(o,t);var r=o.prototype;return r.show=function(){var t=this;if(!B.trigger(this._element,"show.bs.toast").defaultPrevented){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var e=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),B.trigger(t._element,"shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),v(this._element),this._element.classList.add("showing"),this._config.animation){var n=u(this._element);B.one(this._element,"transitionend",e),h(this._element,n)}else e()}},r.hide=function(){var t=this;if(this._element.classList.contains("show")&&!B.trigger(this._element,"hide.bs.toast").defaultPrevented){var e=function(){t._element.classList.add("hide"),B.trigger(t._element,"hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=u(this._element);B.one(this._element,"transitionend",e),h(this._element,n)}else e()}},r.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),B.off(this._element,"click.dismiss.bs.toast"),t.prototype.dispose.call(this),this._config=null},r._getConfig=function(t){return t=n({},Ue,z.getDataAttributes(this._element),"object"==typeof t&&t?t:{}),p("toast",t,this.constructor.DefaultType),t},r._setListeners=function(){var t=this;B.on(this._element,"click.dismiss.bs.toast",'[data-bs-dismiss="toast"]',(function(){return t.hide()}))},r._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},o.jQueryInterface=function(t){return this.each((function(){var e=T(this,"bs.toast");if(e||(e=new o(this,"object"==typeof t&&t)),"string"==typeof t){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t](this)}}))},e(o,null,[{key:"DefaultType",get:function(){return Ke}},{key:"Default",get:function(){return Ue}},{key:"DATA_KEY",get:function(){return"bs.toast"}}]),o}(H);return y("toast",ze),{Alert:R,Button:W,Carousel:$,Collapse:J,Dropdown:ve,Modal:ye,Popover:Me,ScrollSpy:Re,Tab:We,Toast:ze,Tooltip:Se}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/static/js/gantt/frappe-gantt.css b/static/js/gantt/frappe-gantt.css new file mode 100644 index 0000000..9e7afed --- /dev/null +++ b/static/js/gantt/frappe-gantt.css @@ -0,0 +1,119 @@ +.gantt .grid-background { + fill: none; } + +.gantt .grid-header { + fill: #ffffff; + stroke: #e0e0e0; + stroke-width: 1.4; } + +.gantt .grid-row { + fill: #ffffff; } + +.gantt .grid-row:nth-child(even) { + fill: #f5f5f5; } + +.gantt .row-line { + stroke: #ebeff2; } + +.gantt .tick { + stroke: #e0e0e0; + stroke-width: 0.2; } + .gantt .tick.thick { + stroke-width: 0.4; } + +.gantt .today-highlight { + fill: #fcf8e3; + opacity: 0.5; } + +.gantt .arrow { + fill: none; + stroke: #666; + stroke-width: 1.4; } + +.gantt .bar { + fill: #b8c2cc; + stroke: #8D99A6; + stroke-width: 0; + transition: stroke-width .3s ease; + user-select: none; } + +.gantt .bar-progress { + fill: #a3a3ff; } + +.gantt .bar-invalid { + fill: transparent; + stroke: #8D99A6; + stroke-width: 1; + stroke-dasharray: 5; } + .gantt .bar-invalid ~ .bar-label { + fill: #555; } + +.gantt .bar-label { + fill: #fff; + dominant-baseline: central; + text-anchor: middle; + font-size: 12px; + font-weight: lighter; } + .gantt .bar-label.big { + fill: #555; + text-anchor: start; } + +.gantt .handle { + fill: #ddd; + cursor: ew-resize; + opacity: 0; + visibility: hidden; + transition: opacity .3s ease; } + +.gantt .bar-wrapper { + cursor: pointer; + outline: none; } + .gantt .bar-wrapper:hover .bar { + fill: #a9b5c1; } + .gantt .bar-wrapper:hover .bar-progress { + fill: #8a8aff; } + .gantt .bar-wrapper:hover .handle { + visibility: visible; + opacity: 1; } + .gantt .bar-wrapper.active .bar { + fill: #a9b5c1; } + .gantt .bar-wrapper.active .bar-progress { + fill: #8a8aff; } + +.gantt .lower-text, .gantt .upper-text { + font-size: 12px; + text-anchor: middle; } + +.gantt .upper-text { + fill: #555; } + +.gantt .lower-text { + fill: #333; } + +.gantt .hide { + display: none; } + +.gantt-container { + position: relative; + overflow: auto; + font-size: 12px; } + .gantt-container .popup-wrapper { + position: absolute; + top: 0; + left: 0; + background: rgba(0, 0, 0, 0.8); + padding: 0; + color: #959da5; + border-radius: 3px; } + .gantt-container .popup-wrapper .title { + border-bottom: 3px solid #a3a3ff; + padding: 10px; } + .gantt-container .popup-wrapper .subtitle { + padding: 10px; + color: #dfe2e5; } + .gantt-container .popup-wrapper .pointer { + position: absolute; + height: 5px; + margin: 0 0 0 -5px; + border: 5px solid transparent; + border-top-color: rgba(0, 0, 0, 0.8); } diff --git a/static/js/gantt/frappe-gantt.js b/static/js/gantt/frappe-gantt.js new file mode 100644 index 0000000..f3df926 --- /dev/null +++ b/static/js/gantt/frappe-gantt.js @@ -0,0 +1,1962 @@ +var Gantt = (function () { +'use strict'; + +const YEAR = 'year'; +const MONTH = 'month'; +const DAY = 'day'; +const HOUR = 'hour'; +const MINUTE = 'minute'; +const SECOND = 'second'; +const MILLISECOND = 'millisecond'; + +const month_names = { + en: [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ], + es: [ + 'Enero', + 'Febrero', + 'Marzo', + 'Abril', + 'Mayo', + 'Junio', + 'Julio', + 'Agosto', + 'Septiembre', + 'Octubre', + 'Noviembre', + 'Diciembre' + ], + ru: [ + 'Январь', + 'Февраль', + 'Март', + 'Апрель', + 'Май', + 'Июнь', + 'Июль', + 'Август', + 'Сентябрь', + 'Октябрь', + 'Ноябрь', + 'Декабрь' + ], + ptBr: [ + 'Janeiro', + 'Fevereiro', + 'Março', + 'Abril', + 'Maio', + 'Junho', + 'Julho', + 'Agosto', + 'Setembro', + 'Outubro', + 'Novembro', + 'Dezembro' + ], + fr: [ + 'Janvier', + 'Février', + 'Mars', + 'Avril', + 'Mai', + 'Juin', + 'Juillet', + 'Août', + 'Septembre', + 'Octobre', + 'Novembre', + 'Décembre' + ], + tr: [ + 'Ocak', + 'Şubat', + 'Mart', + 'Nisan', + 'Mayıs', + 'Haziran', + 'Temmuz', + 'Ağustos', + 'Eylül', + 'Ekim', + 'Kasım', + 'Aralık' + ], + zh: [ + '一月', + '二月', + '三月', + '四月', + '五月', + '六月', + '七月', + '八月', + '九月', + '十月', + '十一月', + '十二月' + ] +}; + +var date_utils = { + parse(date, date_separator = '-', time_separator = /[.:]/) { + if (date instanceof Date) { + return date; + } + if (typeof date === 'string') { + let date_parts, time_parts; + const parts = date.split(' '); + + date_parts = parts[0] + .split(date_separator) + .map(val => parseInt(val, 10)); + time_parts = parts[1] && parts[1].split(time_separator); + + // month is 0 indexed + date_parts[1] = date_parts[1] - 1; + + let vals = date_parts; + + if (time_parts && time_parts.length) { + if (time_parts.length == 4) { + time_parts[3] = '0.' + time_parts[3]; + time_parts[3] = parseFloat(time_parts[3]) * 1000; + } + vals = vals.concat(time_parts); + } + + return new Date(...vals); + } + }, + + to_string(date, with_time = false) { + if (!(date instanceof Date)) { + throw new TypeError('Invalid argument type'); + } + const vals = this.get_date_values(date).map((val, i) => { + if (i === 1) { + // add 1 for month + val = val + 1; + } + + if (i === 6) { + return padStart(val + '', 3, '0'); + } + + return padStart(val + '', 2, '0'); + }); + const date_string = `${vals[0]}-${vals[1]}-${vals[2]}`; + const time_string = `${vals[3]}:${vals[4]}:${vals[5]}.${vals[6]}`; + + return date_string + (with_time ? ' ' + time_string : ''); + }, + + format(date, format_string = 'YYYY-MM-DD HH:mm:ss.SSS', lang = 'en') { + const values = this.get_date_values(date).map(d => padStart(d, 2, 0)); + const format_map = { + YYYY: values[0], + MM: padStart(+values[1] + 1, 2, 0), + DD: values[2], + HH: values[3], + mm: values[4], + ss: values[5], + SSS:values[6], + D: values[2], + MMMM: month_names[lang][+values[1]], + MMM: month_names[lang][+values[1]] + }; + + let str = format_string; + const formatted_values = []; + + Object.keys(format_map) + .sort((a, b) => b.length - a.length) // big string first + .forEach(key => { + if (str.includes(key)) { + str = str.replace(key, `$${formatted_values.length}`); + formatted_values.push(format_map[key]); + } + }); + + formatted_values.forEach((value, i) => { + str = str.replace(`$${i}`, value); + }); + + return str; + }, + + diff(date_a, date_b, scale = DAY) { + let milliseconds, seconds, hours, minutes, days, months, years; + + milliseconds = date_a - date_b; + seconds = milliseconds / 1000; + minutes = seconds / 60; + hours = minutes / 60; + days = hours / 24; + months = days / 30; + years = months / 12; + + if (!scale.endsWith('s')) { + scale += 's'; + } + + return Math.floor( + { + milliseconds, + seconds, + minutes, + hours, + days, + months, + years + }[scale] + ); + }, + + today() { + const vals = this.get_date_values(new Date()).slice(0, 3); + return new Date(...vals); + }, + + now() { + return new Date(); + }, + + add(date, qty, scale) { + qty = parseInt(qty, 10); + const vals = [ + date.getFullYear() + (scale === YEAR ? qty : 0), + date.getMonth() + (scale === MONTH ? qty : 0), + date.getDate() + (scale === DAY ? qty : 0), + date.getHours() + (scale === HOUR ? qty : 0), + date.getMinutes() + (scale === MINUTE ? qty : 0), + date.getSeconds() + (scale === SECOND ? qty : 0), + date.getMilliseconds() + (scale === MILLISECOND ? qty : 0) + ]; + return new Date(...vals); + }, + + start_of(date, scale) { + const scores = { + [YEAR]: 6, + [MONTH]: 5, + [DAY]: 4, + [HOUR]: 3, + [MINUTE]: 2, + [SECOND]: 1, + [MILLISECOND]: 0 + }; + + function should_reset(_scale) { + const max_score = scores[scale]; + return scores[_scale] <= max_score; + } + + const vals = [ + date.getFullYear(), + should_reset(YEAR) ? 0 : date.getMonth(), + should_reset(MONTH) ? 1 : date.getDate(), + should_reset(DAY) ? 0 : date.getHours(), + should_reset(HOUR) ? 0 : date.getMinutes(), + should_reset(MINUTE) ? 0 : date.getSeconds(), + should_reset(SECOND) ? 0 : date.getMilliseconds() + ]; + + return new Date(...vals); + }, + + clone(date) { + return new Date(...this.get_date_values(date)); + }, + + get_date_values(date) { + return [ + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + date.getMilliseconds() + ]; + }, + + get_days_in_month(date) { + const no_of_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + const month = date.getMonth(); + + if (month !== 1) { + return no_of_days[month]; + } + + // Feb + const year = date.getFullYear(); + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { + return 29; + } + return 28; + } +}; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart +function padStart(str, targetLength, padString) { + str = str + ''; + targetLength = targetLength >> 0; + padString = String(typeof padString !== 'undefined' ? padString : ' '); + if (str.length > targetLength) { + return String(str); + } else { + targetLength = targetLength - str.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); + } + return padString.slice(0, targetLength) + String(str); + } +} + +function $(expr, con) { + return typeof expr === 'string' + ? (con || document).querySelector(expr) + : expr || null; +} + +function createSVG(tag, attrs) { + const elem = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (let attr in attrs) { + if (attr === 'append_to') { + const parent = attrs.append_to; + parent.appendChild(elem); + } else if (attr === 'innerHTML') { + elem.innerHTML = attrs.innerHTML; + } else { + elem.setAttribute(attr, attrs[attr]); + } + } + return elem; +} + +function animateSVG(svgElement, attr, from, to) { + const animatedSvgElement = getAnimationElement(svgElement, attr, from, to); + + if (animatedSvgElement === svgElement) { + // triggered 2nd time programmatically + // trigger artificial click event + const event = document.createEvent('HTMLEvents'); + event.initEvent('click', true, true); + event.eventName = 'click'; + animatedSvgElement.dispatchEvent(event); + } +} + +function getAnimationElement( + svgElement, + attr, + from, + to, + dur = '0.4s', + begin = '0.1s' +) { + const animEl = svgElement.querySelector('animate'); + if (animEl) { + $.attr(animEl, { + attributeName: attr, + from, + to, + dur, + begin: 'click + ' + begin // artificial click + }); + return svgElement; + } + + const animateElement = createSVG('animate', { + attributeName: attr, + from, + to, + dur, + begin, + calcMode: 'spline', + values: from + ';' + to, + keyTimes: '0; 1', + keySplines: cubic_bezier('ease-out') + }); + svgElement.appendChild(animateElement); + + return svgElement; +} + +function cubic_bezier(name) { + return { + ease: '.25 .1 .25 1', + linear: '0 0 1 1', + 'ease-in': '.42 0 1 1', + 'ease-out': '0 0 .58 1', + 'ease-in-out': '.42 0 .58 1' + }[name]; +} + +$.on = (element, event, selector, callback) => { + if (!callback) { + callback = selector; + $.bind(element, event, callback); + } else { + $.delegate(element, event, selector, callback); + } +}; + +$.off = (element, event, handler) => { + element.removeEventListener(event, handler); +}; + +$.bind = (element, event, callback) => { + event.split(/\s+/).forEach(function(event) { + element.addEventListener(event, callback); + }); +}; + +$.delegate = (element, event, selector, callback) => { + element.addEventListener(event, function(e) { + const delegatedTarget = e.target.closest(selector); + if (delegatedTarget) { + e.delegatedTarget = delegatedTarget; + callback.call(this, e, delegatedTarget); + } + }); +}; + +$.closest = (selector, element) => { + if (!element) return null; + + if (element.matches(selector)) { + return element; + } + + return $.closest(selector, element.parentNode); +}; + +$.attr = (element, attr, value) => { + if (!value && typeof attr === 'string') { + return element.getAttribute(attr); + } + + if (typeof attr === 'object') { + for (let key in attr) { + $.attr(element, key, attr[key]); + } + return; + } + + element.setAttribute(attr, value); +}; + +class Bar { + constructor(gantt, task) { + this.set_defaults(gantt, task); + this.prepare(); + this.draw(); + this.bind(); + } + + set_defaults(gantt, task) { + this.action_completed = false; + this.gantt = gantt; + this.task = task; + } + + prepare() { + this.prepare_values(); + this.prepare_helpers(); + } + + prepare_values() { + this.invalid = this.task.invalid; + this.height = this.gantt.options.bar_height; + this.x = this.compute_x(); + this.y = this.compute_y(); + this.corner_radius = this.gantt.options.bar_corner_radius; + this.duration = + date_utils.diff(this.task._end, this.task._start, 'hour') / + this.gantt.options.step; + this.width = this.gantt.options.column_width * this.duration; + this.progress_width = + this.gantt.options.column_width * + this.duration * + (this.task.progress / 100) || 0; + this.group = createSVG('g', { + class: 'bar-wrapper ' + (this.task.custom_class || ''), + 'data-id': this.task.id + }); + this.bar_group = createSVG('g', { + class: 'bar-group', + append_to: this.group + }); + this.handle_group = createSVG('g', { + class: 'handle-group', + append_to: this.group + }); + } + + prepare_helpers() { + SVGElement.prototype.getX = function() { + return +this.getAttribute('x'); + }; + SVGElement.prototype.getY = function() { + return +this.getAttribute('y'); + }; + SVGElement.prototype.getWidth = function() { + return +this.getAttribute('width'); + }; + SVGElement.prototype.getHeight = function() { + return +this.getAttribute('height'); + }; + SVGElement.prototype.getEndX = function() { + return this.getX() + this.getWidth(); + }; + } + + draw() { + this.draw_bar(); + this.draw_progress_bar(); + this.draw_label(); + this.draw_resize_handles(); + } + + draw_bar() { + this.$bar = createSVG('rect', { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + rx: this.corner_radius, + ry: this.corner_radius, + class: 'bar', + append_to: this.bar_group + }); + + animateSVG(this.$bar, 'width', 0, this.width); + + if (this.invalid) { + this.$bar.classList.add('bar-invalid'); + } + } + + draw_progress_bar() { + if (this.invalid) return; + this.$bar_progress = createSVG('rect', { + x: this.x, + y: this.y, + width: this.progress_width, + height: this.height, + rx: this.corner_radius, + ry: this.corner_radius, + class: 'bar-progress', + append_to: this.bar_group + }); + + animateSVG(this.$bar_progress, 'width', 0, this.progress_width); + } + + draw_label() { + createSVG('text', { + x: this.x + this.width / 2, + y: this.y + this.height / 2, + innerHTML: this.task.name, + class: 'bar-label', + append_to: this.bar_group + }); + // labels get BBox in the next tick + requestAnimationFrame(() => this.update_label_position()); + } + + draw_resize_handles() { + if (this.invalid) return; + + const bar = this.$bar; + const handle_width = 8; + + createSVG('rect', { + x: bar.getX() + bar.getWidth() - 9, + y: bar.getY() + 1, + width: handle_width, + height: this.height - 2, + rx: this.corner_radius, + ry: this.corner_radius, + class: 'handle right', + append_to: this.handle_group + }); + + createSVG('rect', { + x: bar.getX() + 1, + y: bar.getY() + 1, + width: handle_width, + height: this.height - 2, + rx: this.corner_radius, + ry: this.corner_radius, + class: 'handle left', + append_to: this.handle_group + }); + + if (this.task.progress && this.task.progress < 100) { + this.$handle_progress = createSVG('polygon', { + points: this.get_progress_polygon_points().join(','), + class: 'handle progress', + append_to: this.handle_group + }); + } + } + + get_progress_polygon_points() { + const bar_progress = this.$bar_progress; + return [ + bar_progress.getEndX() - 5, + bar_progress.getY() + bar_progress.getHeight(), + bar_progress.getEndX() + 5, + bar_progress.getY() + bar_progress.getHeight(), + bar_progress.getEndX(), + bar_progress.getY() + bar_progress.getHeight() - 8.66 + ]; + } + + bind() { + if (this.invalid) return; + this.setup_click_event(); + } + + setup_click_event() { + $.on(this.group, 'focus ' + this.gantt.options.popup_trigger, e => { + if (this.action_completed) { + // just finished a move action, wait for a few seconds + return; + } + + this.show_popup(); + this.gantt.unselect_all(); + this.group.classList.add('active'); + }); + + $.on(this.group, 'dblclick', e => { + if (this.action_completed) { + // just finished a move action, wait for a few seconds + return; + } + + this.gantt.trigger_event('click', [this.task]); + }); + } + + show_popup() { + if (this.gantt.bar_being_dragged) return; + + const start_date = date_utils.format(this.task._start, 'MMM D', this.gantt.options.language); + const end_date = date_utils.format( + date_utils.add(this.task._end, -1, 'second'), + 'MMM D', + this.gantt.options.language + ); + const subtitle = start_date + ' - ' + end_date; + + this.gantt.show_popup({ + target_element: this.$bar, + title: this.task.name, + subtitle: subtitle, + task: this.task, + }); + } + + update_bar_position({ x = null, width = null }) { + const bar = this.$bar; + if (x) { + // get all x values of parent task + const xs = this.task.dependencies.map(dep => { + return this.gantt.get_bar(dep).$bar.getX(); + }); + // child task must not go before parent + const valid_x = xs.reduce((prev, curr) => { + return x >= curr; + }, x); + if (!valid_x) { + width = null; + return; + } + this.update_attr(bar, 'x', x); + } + if (width && width >= this.gantt.options.column_width) { + this.update_attr(bar, 'width', width); + } + this.update_label_position(); + this.update_handle_position(); + this.update_progressbar_position(); + this.update_arrow_position(); + } + + date_changed() { + let changed = false; + const { new_start_date, new_end_date } = this.compute_start_end_date(); + + if (Number(this.task._start) !== Number(new_start_date)) { + changed = true; + this.task._start = new_start_date; + } + + if (Number(this.task._end) !== Number(new_end_date)) { + changed = true; + this.task._end = new_end_date; + } + + if (!changed) return; + + this.gantt.trigger_event('date_change', [ + this.task, + new_start_date, + date_utils.add(new_end_date, -1, 'second') + ]); + } + + progress_changed() { + const new_progress = this.compute_progress(); + this.task.progress = new_progress; + this.gantt.trigger_event('progress_change', [this.task, new_progress]); + } + + set_action_completed() { + this.action_completed = true; + setTimeout(() => (this.action_completed = false), 1000); + } + + compute_start_end_date() { + const bar = this.$bar; + const x_in_units = bar.getX() / this.gantt.options.column_width; + const new_start_date = date_utils.add( + this.gantt.gantt_start, + x_in_units * this.gantt.options.step, + 'hour' + ); + const width_in_units = bar.getWidth() / this.gantt.options.column_width; + const new_end_date = date_utils.add( + new_start_date, + width_in_units * this.gantt.options.step, + 'hour' + ); + + return { new_start_date, new_end_date }; + } + + compute_progress() { + const progress = + this.$bar_progress.getWidth() / this.$bar.getWidth() * 100; + return parseInt(progress, 10); + } + + compute_x() { + const { step, column_width } = this.gantt.options; + const task_start = this.task._start; + const gantt_start = this.gantt.gantt_start; + + const diff = date_utils.diff(task_start, gantt_start, 'hour'); + let x = diff / step * column_width; + + if (this.gantt.view_is('Month')) { + const diff = date_utils.diff(task_start, gantt_start, 'day'); + x = diff * column_width / 30; + } + return x; + } + + compute_y() { + return ( + this.gantt.options.header_height + + this.gantt.options.padding + + this.task._index * (this.height + this.gantt.options.padding) + ); + } + + get_snap_position(dx) { + let odx = dx, + rem, + position; + + if (this.gantt.view_is('Week')) { + rem = dx % (this.gantt.options.column_width / 7); + position = + odx - + rem + + (rem < this.gantt.options.column_width / 14 + ? 0 + : this.gantt.options.column_width / 7); + } else if (this.gantt.view_is('Month')) { + rem = dx % (this.gantt.options.column_width / 30); + position = + odx - + rem + + (rem < this.gantt.options.column_width / 60 + ? 0 + : this.gantt.options.column_width / 30); + } else { + rem = dx % this.gantt.options.column_width; + position = + odx - + rem + + (rem < this.gantt.options.column_width / 2 + ? 0 + : this.gantt.options.column_width); + } + return position; + } + + update_attr(element, attr, value) { + value = +value; + if (!isNaN(value)) { + element.setAttribute(attr, value); + } + return element; + } + + update_progressbar_position() { + this.$bar_progress.setAttribute('x', this.$bar.getX()); + this.$bar_progress.setAttribute( + 'width', + this.$bar.getWidth() * (this.task.progress / 100) + ); + } + + update_label_position() { + const bar = this.$bar, + label = this.group.querySelector('.bar-label'); + + if (label.getBBox().width > bar.getWidth()) { + label.classList.add('big'); + label.setAttribute('x', bar.getX() + bar.getWidth() + 5); + } else { + label.classList.remove('big'); + label.setAttribute('x', bar.getX() + bar.getWidth() / 2); + } + } + + update_handle_position() { + const bar = this.$bar; + this.handle_group + .querySelector('.handle.left') + .setAttribute('x', bar.getX() + 1); + this.handle_group + .querySelector('.handle.right') + .setAttribute('x', bar.getEndX() - 9); + const handle = this.group.querySelector('.handle.progress'); + handle && + handle.setAttribute('points', this.get_progress_polygon_points()); + } + + update_arrow_position() { + this.arrows = this.arrows || []; + for (let arrow of this.arrows) { + arrow.update(); + } + } +} + +class Arrow { + constructor(gantt, from_task, to_task) { + this.gantt = gantt; + this.from_task = from_task; + this.to_task = to_task; + + this.calculate_path(); + this.draw(); + } + + calculate_path() { + let start_x = + this.from_task.$bar.getX() + this.from_task.$bar.getWidth() / 2; + + const condition = () => + this.to_task.$bar.getX() < start_x + this.gantt.options.padding && + start_x > this.from_task.$bar.getX() + this.gantt.options.padding; + + while (condition()) { + start_x -= 10; + } + + const start_y = + this.gantt.options.header_height + + this.gantt.options.bar_height + + (this.gantt.options.padding + this.gantt.options.bar_height) * + this.from_task.task._index + + this.gantt.options.padding; + + const end_x = this.to_task.$bar.getX() - this.gantt.options.padding / 2; + const end_y = + this.gantt.options.header_height + + this.gantt.options.bar_height / 2 + + (this.gantt.options.padding + this.gantt.options.bar_height) * + this.to_task.task._index + + this.gantt.options.padding; + + const from_is_below_to = + this.from_task.task._index > this.to_task.task._index; + const curve = this.gantt.options.arrow_curve; + const clockwise = from_is_below_to ? 1 : 0; + const curve_y = from_is_below_to ? -curve : curve; + const offset = from_is_below_to + ? end_y + this.gantt.options.arrow_curve + : end_y - this.gantt.options.arrow_curve; + + this.path = ` + M ${start_x} ${start_y} + V ${offset} + a ${curve} ${curve} 0 0 ${clockwise} ${curve} ${curve_y} + L ${end_x} ${end_y} + m -5 -5 + l 5 5 + l -5 5`; + + if ( + this.to_task.$bar.getX() < + this.from_task.$bar.getX() + this.gantt.options.padding + ) { + const down_1 = this.gantt.options.padding / 2 - curve; + const down_2 = + this.to_task.$bar.getY() + + this.to_task.$bar.getHeight() / 2 - + curve_y; + const left = this.to_task.$bar.getX() - this.gantt.options.padding; + + this.path = ` + M ${start_x} ${start_y} + v ${down_1} + a ${curve} ${curve} 0 0 1 -${curve} ${curve} + H ${left} + a ${curve} ${curve} 0 0 ${clockwise} -${curve} ${curve_y} + V ${down_2} + a ${curve} ${curve} 0 0 ${clockwise} ${curve} ${curve_y} + L ${end_x} ${end_y} + m -5 -5 + l 5 5 + l -5 5`; + } + } + + draw() { + this.element = createSVG('path', { + d: this.path, + 'data-from': this.from_task.task.id, + 'data-to': this.to_task.task.id + }); + } + + update() { + this.calculate_path(); + this.element.setAttribute('d', this.path); + } +} + +class Popup { + constructor(parent, custom_html) { + this.parent = parent; + this.custom_html = custom_html; + this.make(); + } + + make() { + this.parent.innerHTML = ` +
+
+
+ `; + + this.hide(); + + this.title = this.parent.querySelector('.title'); + this.subtitle = this.parent.querySelector('.subtitle'); + this.pointer = this.parent.querySelector('.pointer'); + } + + show(options) { + if (!options.target_element) { + throw new Error('target_element is required to show popup'); + } + if (!options.position) { + options.position = 'left'; + } + const target_element = options.target_element; + + if (this.custom_html) { + let html = this.custom_html(options.task); + html += '
'; + this.parent.innerHTML = html; + this.pointer = this.parent.querySelector('.pointer'); + } else { + // set data + this.title.innerHTML = options.title; + this.subtitle.innerHTML = options.subtitle; + this.parent.style.width = this.parent.clientWidth + 'px'; + } + + // set position + let position_meta; + if (target_element instanceof HTMLElement) { + position_meta = target_element.getBoundingClientRect(); + } else if (target_element instanceof SVGElement) { + position_meta = options.target_element.getBBox(); + } + + if (options.position === 'left') { + this.parent.style.left = + position_meta.x + (position_meta.width + 10) + 'px'; + this.parent.style.top = position_meta.y + 'px'; + + this.pointer.style.transform = 'rotateZ(90deg)'; + this.pointer.style.left = '-7px'; + this.pointer.style.top = '2px'; + } + + // show + this.parent.style.opacity = 1; + } + + hide() { + this.parent.style.opacity = 0; + } +} + +const VIEW_MODE = { + QUARTER_DAY: 'Quarter Day', + HALF_DAY: 'Half Day', + DAY: 'Day', + WEEK: 'Week', + MONTH: 'Month', + YEAR: 'Year' +}; + +class Gantt { + constructor(wrapper, tasks, options) { + this.setup_wrapper(wrapper); + this.setup_options(options); + this.setup_tasks(tasks); + // initialize with default view mode + this.change_view_mode(); + this.bind_events(); + } + + setup_wrapper(element) { + let svg_element, wrapper_element; + + // CSS Selector is passed + if (typeof element === 'string') { + element = document.querySelector(element); + } + + // get the SVGElement + if (element instanceof HTMLElement) { + wrapper_element = element; + svg_element = element.querySelector('svg'); + } else if (element instanceof SVGElement) { + svg_element = element; + } else { + throw new TypeError( + 'Frappé Gantt only supports usage of a string CSS selector,' + + " HTML DOM element or SVG DOM element for the 'element' parameter" + ); + } + + // svg element + if (!svg_element) { + // create it + this.$svg = createSVG('svg', { + append_to: wrapper_element, + class: 'gantt' + }); + } else { + this.$svg = svg_element; + this.$svg.classList.add('gantt'); + } + + // wrapper element + this.$container = document.createElement('div'); + this.$container.classList.add('gantt-container'); + + const parent_element = this.$svg.parentElement; + parent_element.appendChild(this.$container); + this.$container.appendChild(this.$svg); + + // popup wrapper + this.popup_wrapper = document.createElement('div'); + this.popup_wrapper.classList.add('popup-wrapper'); + this.$container.appendChild(this.popup_wrapper); + } + + setup_options(options) { + const default_options = { + header_height: 50, + column_width: 30, + step: 24, + view_modes: [...Object.values(VIEW_MODE)], + bar_height: 20, + bar_corner_radius: 3, + arrow_curve: 5, + padding: 18, + view_mode: 'Day', + date_format: 'YYYY-MM-DD', + popup_trigger: 'click', + custom_popup_html: null, + language: 'en' + }; + this.options = Object.assign({}, default_options, options); + } + + setup_tasks(tasks) { + // prepare tasks + this.tasks = tasks.map((task, i) => { + // convert to Date objects + task._start = date_utils.parse(task.start); + task._end = date_utils.parse(task.end); + + // make task invalid if duration too large + if (date_utils.diff(task._end, task._start, 'year') > 10) { + task.end = null; + } + + // cache index + task._index = i; + + // invalid dates + if (!task.start && !task.end) { + const today = date_utils.today(); + task._start = today; + task._end = date_utils.add(today, 2, 'day'); + } + + if (!task.start && task.end) { + task._start = date_utils.add(task._end, -2, 'day'); + } + + if (task.start && !task.end) { + task._end = date_utils.add(task._start, 2, 'day'); + } + + // if hours is not set, assume the last day is full day + // e.g: 2018-09-09 becomes 2018-09-09 23:59:59 + const task_end_values = date_utils.get_date_values(task._end); + if (task_end_values.slice(3).every(d => d === 0)) { + task._end = date_utils.add(task._end, 24, 'hour'); + } + + // invalid flag + if (!task.start || !task.end) { + task.invalid = true; + } + + // dependencies + if (typeof task.dependencies === 'string' || !task.dependencies) { + let deps = []; + if (task.dependencies) { + deps = task.dependencies + .split(',') + .map(d => d.trim()) + .filter(d => d); + } + task.dependencies = deps; + } + + // uids + if (!task.id) { + task.id = generate_id(task); + } + + return task; + }); + + this.setup_dependencies(); + } + + setup_dependencies() { + this.dependency_map = {}; + for (let t of this.tasks) { + for (let d of t.dependencies) { + this.dependency_map[d] = this.dependency_map[d] || []; + this.dependency_map[d].push(t.id); + } + } + } + + refresh(tasks) { + this.setup_tasks(tasks); + this.change_view_mode(); + } + + change_view_mode(mode = this.options.view_mode) { + this.update_view_scale(mode); + this.setup_dates(); + this.render(); + // fire viewmode_change event + this.trigger_event('view_change', [mode]); + } + + update_view_scale(view_mode) { + this.options.view_mode = view_mode; + + if (view_mode === VIEW_MODE.DAY) { + this.options.step = 24; + this.options.column_width = 38; + } else if (view_mode === VIEW_MODE.HALF_DAY) { + this.options.step = 24 / 2; + this.options.column_width = 38; + } else if (view_mode === VIEW_MODE.QUARTER_DAY) { + this.options.step = 24 / 4; + this.options.column_width = 38; + } else if (view_mode === VIEW_MODE.WEEK) { + this.options.step = 24 * 7; + this.options.column_width = 140; + } else if (view_mode === VIEW_MODE.MONTH) { + this.options.step = 24 * 30; + this.options.column_width = 120; + } else if (view_mode === VIEW_MODE.YEAR) { + this.options.step = 24 * 365; + this.options.column_width = 120; + } + } + + setup_dates() { + this.setup_gantt_dates(); + this.setup_date_values(); + } + + setup_gantt_dates() { + this.gantt_start = this.gantt_end = null; + + for (let task of this.tasks) { + // set global start and end date + if (!this.gantt_start || task._start < this.gantt_start) { + this.gantt_start = task._start; + } + if (!this.gantt_end || task._end > this.gantt_end) { + this.gantt_end = task._end; + } + } + + this.gantt_start = date_utils.start_of(this.gantt_start, 'day'); + this.gantt_end = date_utils.start_of(this.gantt_end, 'day'); + + // add date padding on both sides + if (this.view_is([VIEW_MODE.QUARTER_DAY, VIEW_MODE.HALF_DAY])) { + this.gantt_start = date_utils.add(this.gantt_start, -7, 'day'); + this.gantt_end = date_utils.add(this.gantt_end, 7, 'day'); + } else if (this.view_is(VIEW_MODE.MONTH)) { + this.gantt_start = date_utils.start_of(this.gantt_start, 'year'); + this.gantt_end = date_utils.add(this.gantt_end, 1, 'year'); + } else if (this.view_is(VIEW_MODE.YEAR)) { + this.gantt_start = date_utils.add(this.gantt_start, -2, 'year'); + this.gantt_end = date_utils.add(this.gantt_end, 2, 'year'); + } else { + this.gantt_start = date_utils.add(this.gantt_start, -1, 'month'); + this.gantt_end = date_utils.add(this.gantt_end, 1, 'month'); + } + } + + setup_date_values() { + this.dates = []; + let cur_date = null; + + while (cur_date === null || cur_date < this.gantt_end) { + if (!cur_date) { + cur_date = date_utils.clone(this.gantt_start); + } else { + if (this.view_is(VIEW_MODE.YEAR)) { + cur_date = date_utils.add(cur_date, 1, 'year'); + } else if (this.view_is(VIEW_MODE.MONTH)) { + cur_date = date_utils.add(cur_date, 1, 'month'); + } else { + cur_date = date_utils.add( + cur_date, + this.options.step, + 'hour' + ); + } + } + this.dates.push(cur_date); + } + } + + bind_events() { + this.bind_grid_click(); + this.bind_bar_events(); + } + + render() { + this.clear(); + this.setup_layers(); + this.make_grid(); + this.make_dates(); + this.make_bars(); + this.make_arrows(); + this.map_arrows_on_bars(); + this.set_width(); + this.set_scroll_position(); + } + + setup_layers() { + this.layers = {}; + const layers = ['grid', 'date', 'arrow', 'progress', 'bar', 'details']; + // make group layers + for (let layer of layers) { + this.layers[layer] = createSVG('g', { + class: layer, + append_to: this.$svg + }); + } + } + + make_grid() { + this.make_grid_background(); + this.make_grid_rows(); + this.make_grid_header(); + this.make_grid_ticks(); + this.make_grid_highlights(); + } + + make_grid_background() { + const grid_width = this.dates.length * this.options.column_width; + const grid_height = + this.options.header_height + + this.options.padding + + (this.options.bar_height + this.options.padding) * + this.tasks.length; + + createSVG('rect', { + x: 0, + y: 0, + width: grid_width, + height: grid_height, + class: 'grid-background', + append_to: this.layers.grid + }); + + $.attr(this.$svg, { + height: grid_height + this.options.padding + 100, + width: '100%' + }); + } + + make_grid_rows() { + const rows_layer = createSVG('g', { append_to: this.layers.grid }); + const lines_layer = createSVG('g', { append_to: this.layers.grid }); + + const row_width = this.dates.length * this.options.column_width; + const row_height = this.options.bar_height + this.options.padding; + + let row_y = this.options.header_height + this.options.padding / 2; + + for (let task of this.tasks) { + createSVG('rect', { + x: 0, + y: row_y, + width: row_width, + height: row_height, + class: 'grid-row', + append_to: rows_layer + }); + + createSVG('line', { + x1: 0, + y1: row_y + row_height, + x2: row_width, + y2: row_y + row_height, + class: 'row-line', + append_to: lines_layer + }); + + row_y += this.options.bar_height + this.options.padding; + } + } + + make_grid_header() { + const header_width = this.dates.length * this.options.column_width; + const header_height = this.options.header_height + 10; + createSVG('rect', { + x: 0, + y: 0, + width: header_width, + height: header_height, + class: 'grid-header', + append_to: this.layers.grid + }); + } + + make_grid_ticks() { + let tick_x = 0; + let tick_y = this.options.header_height + this.options.padding / 2; + let tick_height = + (this.options.bar_height + this.options.padding) * + this.tasks.length; + + for (let date of this.dates) { + let tick_class = 'tick'; + // thick tick for monday + if (this.view_is(VIEW_MODE.DAY) && date.getDate() === 1) { + tick_class += ' thick'; + } + // thick tick for first week + if ( + this.view_is(VIEW_MODE.WEEK) && + date.getDate() >= 1 && + date.getDate() < 8 + ) { + tick_class += ' thick'; + } + // thick ticks for quarters + if (this.view_is(VIEW_MODE.MONTH) && (date.getMonth() + 1) % 3 === 0) { + tick_class += ' thick'; + } + + createSVG('path', { + d: `M ${tick_x} ${tick_y} v ${tick_height}`, + class: tick_class, + append_to: this.layers.grid + }); + + if (this.view_is(VIEW_MODE.MONTH)) { + tick_x += + date_utils.get_days_in_month(date) * + this.options.column_width / + 30; + } else { + tick_x += this.options.column_width; + } + } + } + + make_grid_highlights() { + // highlight today's date + if (this.view_is(VIEW_MODE.DAY)) { + const x = + date_utils.diff(date_utils.today(), this.gantt_start, 'hour') / + this.options.step * + this.options.column_width; + const y = 0; + + const width = this.options.column_width; + const height = + (this.options.bar_height + this.options.padding) * + this.tasks.length + + this.options.header_height + + this.options.padding / 2; + + createSVG('rect', { + x, + y, + width, + height, + class: 'today-highlight', + append_to: this.layers.grid + }); + } + } + + make_dates() { + for (let date of this.get_dates_to_draw()) { + createSVG('text', { + x: date.lower_x, + y: date.lower_y, + innerHTML: date.lower_text, + class: 'lower-text', + append_to: this.layers.date + }); + + if (date.upper_text) { + const $upper_text = createSVG('text', { + x: date.upper_x, + y: date.upper_y, + innerHTML: date.upper_text, + class: 'upper-text', + append_to: this.layers.date + }); + + // remove out-of-bound dates + if ( + $upper_text.getBBox().x2 > this.layers.grid.getBBox().width + ) { + $upper_text.remove(); + } + } + } + } + + get_dates_to_draw() { + let last_date = null; + const dates = this.dates.map((date, i) => { + const d = this.get_date_info(date, last_date, i); + last_date = date; + return d; + }); + return dates; + } + + get_date_info(date, last_date, i) { + if (!last_date) { + last_date = date_utils.add(date, 1, 'year'); + } + const date_text = { + 'Quarter Day_lower': date_utils.format( + date, + 'HH', + this.options.language + ), + 'Half Day_lower': date_utils.format( + date, + 'HH', + this.options.language + ), + Day_lower: + date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D', this.options.language) + : '', + Week_lower: + date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'D MMM', this.options.language) + : date_utils.format(date, 'D', this.options.language), + Month_lower: date_utils.format(date, 'MMMM', this.options.language), + Year_lower: date_utils.format(date, 'YYYY', this.options.language), + 'Quarter Day_upper': + date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D MMM', this.options.language) + : '', + 'Half Day_upper': + date.getDate() !== last_date.getDate() + ? date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'D MMM', this.options.language) + : date_utils.format(date, 'D', this.options.language) + : '', + Day_upper: + date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'MMMM', this.options.language) + : '', + Week_upper: + date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'MMMM', this.options.language) + : '', + Month_upper: + date.getFullYear() !== last_date.getFullYear() + ? date_utils.format(date, 'YYYY', this.options.language) + : '', + Year_upper: + date.getFullYear() !== last_date.getFullYear() + ? date_utils.format(date, 'YYYY', this.options.language) + : '' + }; + + const base_pos = { + x: i * this.options.column_width, + lower_y: this.options.header_height, + upper_y: this.options.header_height - 25 + }; + + const x_pos = { + 'Quarter Day_lower': this.options.column_width * 4 / 2, + 'Quarter Day_upper': 0, + 'Half Day_lower': this.options.column_width * 2 / 2, + 'Half Day_upper': 0, + Day_lower: this.options.column_width / 2, + Day_upper: this.options.column_width * 30 / 2, + Week_lower: 0, + Week_upper: this.options.column_width * 4 / 2, + Month_lower: this.options.column_width / 2, + Month_upper: this.options.column_width * 12 / 2, + Year_lower: this.options.column_width / 2, + Year_upper: this.options.column_width * 30 / 2 + }; + + return { + upper_text: date_text[`${this.options.view_mode}_upper`], + lower_text: date_text[`${this.options.view_mode}_lower`], + upper_x: base_pos.x + x_pos[`${this.options.view_mode}_upper`], + upper_y: base_pos.upper_y, + lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`], + lower_y: base_pos.lower_y + }; + } + + make_bars() { + this.bars = this.tasks.map(task => { + const bar = new Bar(this, task); + this.layers.bar.appendChild(bar.group); + return bar; + }); + } + + make_arrows() { + this.arrows = []; + for (let task of this.tasks) { + let arrows = []; + arrows = task.dependencies + .map(task_id => { + const dependency = this.get_task(task_id); + if (!dependency) return; + const arrow = new Arrow( + this, + this.bars[dependency._index], // from_task + this.bars[task._index] // to_task + ); + this.layers.arrow.appendChild(arrow.element); + return arrow; + }) + .filter(Boolean); // filter falsy values + this.arrows = this.arrows.concat(arrows); + } + } + + map_arrows_on_bars() { + for (let bar of this.bars) { + bar.arrows = this.arrows.filter(arrow => { + return ( + arrow.from_task.task.id === bar.task.id || + arrow.to_task.task.id === bar.task.id + ); + }); + } + } + + set_width() { + const cur_width = this.$svg.getBoundingClientRect().width; + const actual_width = this.$svg + .querySelector('.grid .grid-row') + .getAttribute('width'); + if (cur_width < actual_width) { + this.$svg.setAttribute('width', actual_width); + } + } + + set_scroll_position() { + const parent_element = this.$svg.parentElement; + if (!parent_element) return; + + const hours_before_first_task = date_utils.diff( + this.get_oldest_starting_date(), + this.gantt_start, + 'hour' + ); + + const scroll_pos = + hours_before_first_task / + this.options.step * + this.options.column_width - + this.options.column_width; + + parent_element.scrollLeft = scroll_pos; + } + + bind_grid_click() { + $.on( + this.$svg, + this.options.popup_trigger, + '.grid-row, .grid-header', + () => { + this.unselect_all(); + this.hide_popup(); + } + ); + } + + bind_bar_events() { + let is_dragging = false; + let x_on_start = 0; + let y_on_start = 0; + let is_resizing_left = false; + let is_resizing_right = false; + let parent_bar_id = null; + let bars = []; // instanceof Bar + this.bar_being_dragged = null; + + function action_in_progress() { + return is_dragging || is_resizing_left || is_resizing_right; + } + + $.on(this.$svg, 'mousedown', '.bar-wrapper, .handle', (e, element) => { + const bar_wrapper = $.closest('.bar-wrapper', element); + + if (element.classList.contains('left')) { + is_resizing_left = true; + } else if (element.classList.contains('right')) { + is_resizing_right = true; + } else if (element.classList.contains('bar-wrapper')) { + is_dragging = true; + } + + bar_wrapper.classList.add('active'); + + x_on_start = e.offsetX; + y_on_start = e.offsetY; + + parent_bar_id = bar_wrapper.getAttribute('data-id'); + const ids = [ + parent_bar_id, + ...this.get_all_dependent_tasks(parent_bar_id) + ]; + bars = ids.map(id => this.get_bar(id)); + + this.bar_being_dragged = parent_bar_id; + + bars.forEach(bar => { + const $bar = bar.$bar; + $bar.ox = $bar.getX(); + $bar.oy = $bar.getY(); + $bar.owidth = $bar.getWidth(); + $bar.finaldx = 0; + }); + }); + + $.on(this.$svg, 'mousemove', e => { + if (!action_in_progress()) return; + const dx = e.offsetX - x_on_start; + const dy = e.offsetY - y_on_start; + + bars.forEach(bar => { + const $bar = bar.$bar; + $bar.finaldx = this.get_snap_position(dx); + + if (is_resizing_left) { + if (parent_bar_id === bar.task.id) { + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx, + width: $bar.owidth - $bar.finaldx + }); + } else { + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx + }); + } + } else if (is_resizing_right) { + if (parent_bar_id === bar.task.id) { + bar.update_bar_position({ + width: $bar.owidth + $bar.finaldx + }); + } + } else if (is_dragging) { + bar.update_bar_position({ x: $bar.ox + $bar.finaldx }); + } + }); + }); + + document.addEventListener('mouseup', e => { + if (is_dragging || is_resizing_left || is_resizing_right) { + bars.forEach(bar => bar.group.classList.remove('active')); + } + + is_dragging = false; + is_resizing_left = false; + is_resizing_right = false; + }); + + $.on(this.$svg, 'mouseup', e => { + this.bar_being_dragged = null; + bars.forEach(bar => { + const $bar = bar.$bar; + if (!$bar.finaldx) return; + bar.date_changed(); + bar.set_action_completed(); + }); + }); + + this.bind_bar_progress(); + } + + bind_bar_progress() { + let x_on_start = 0; + let y_on_start = 0; + let is_resizing = null; + let bar = null; + let $bar_progress = null; + let $bar = null; + + $.on(this.$svg, 'mousedown', '.handle.progress', (e, handle) => { + is_resizing = true; + x_on_start = e.offsetX; + y_on_start = e.offsetY; + + const $bar_wrapper = $.closest('.bar-wrapper', handle); + const id = $bar_wrapper.getAttribute('data-id'); + bar = this.get_bar(id); + + $bar_progress = bar.$bar_progress; + $bar = bar.$bar; + + $bar_progress.finaldx = 0; + $bar_progress.owidth = $bar_progress.getWidth(); + $bar_progress.min_dx = -$bar_progress.getWidth(); + $bar_progress.max_dx = $bar.getWidth() - $bar_progress.getWidth(); + }); + + $.on(this.$svg, 'mousemove', e => { + if (!is_resizing) return; + let dx = e.offsetX - x_on_start; + let dy = e.offsetY - y_on_start; + + if (dx > $bar_progress.max_dx) { + dx = $bar_progress.max_dx; + } + if (dx < $bar_progress.min_dx) { + dx = $bar_progress.min_dx; + } + + const $handle = bar.$handle_progress; + $.attr($bar_progress, 'width', $bar_progress.owidth + dx); + $.attr($handle, 'points', bar.get_progress_polygon_points()); + $bar_progress.finaldx = dx; + }); + + $.on(this.$svg, 'mouseup', () => { + is_resizing = false; + if (!($bar_progress && $bar_progress.finaldx)) return; + bar.progress_changed(); + bar.set_action_completed(); + }); + } + + get_all_dependent_tasks(task_id) { + let out = []; + let to_process = [task_id]; + while (to_process.length) { + const deps = to_process.reduce((acc, curr) => { + acc = acc.concat(this.dependency_map[curr]); + return acc; + }, []); + + out = out.concat(deps); + to_process = deps.filter(d => !to_process.includes(d)); + } + + return out.filter(Boolean); + } + + get_snap_position(dx) { + let odx = dx, + rem, + position; + + if (this.view_is(VIEW_MODE.WEEK)) { + rem = dx % (this.options.column_width / 7); + position = + odx - + rem + + (rem < this.options.column_width / 14 + ? 0 + : this.options.column_width / 7); + } else if (this.view_is(VIEW_MODE.MONTH)) { + rem = dx % (this.options.column_width / 30); + position = + odx - + rem + + (rem < this.options.column_width / 60 + ? 0 + : this.options.column_width / 30); + } else { + rem = dx % this.options.column_width; + position = + odx - + rem + + (rem < this.options.column_width / 2 + ? 0 + : this.options.column_width); + } + return position; + } + + unselect_all() { + [...this.$svg.querySelectorAll('.bar-wrapper')].forEach(el => { + el.classList.remove('active'); + }); + } + + view_is(modes) { + if (typeof modes === 'string') { + return this.options.view_mode === modes; + } + + if (Array.isArray(modes)) { + return modes.some(mode => this.options.view_mode === mode); + } + + return false; + } + + get_task(id) { + return this.tasks.find(task => { + return task.id === id; + }); + } + + get_bar(id) { + return this.bars.find(bar => { + return bar.task.id === id; + }); + } + + show_popup(options) { + if (!this.popup) { + this.popup = new Popup( + this.popup_wrapper, + this.options.custom_popup_html + ); + } + this.popup.show(options); + } + + hide_popup() { + this.popup && this.popup.hide(); + } + + trigger_event(event, args) { + if (this.options['on_' + event]) { + this.options['on_' + event].apply(null, args); + } + } + + /** + * Gets the oldest starting date from the list of tasks + * + * @returns Date + * @memberof Gantt + */ + get_oldest_starting_date() { + return this.tasks + .map(task => task._start) + .reduce( + (prev_date, cur_date) => + cur_date <= prev_date ? cur_date : prev_date + ); + } + + /** + * Clear all elements from the parent svg element + * + * @memberof Gantt + */ + clear() { + this.$svg.innerHTML = ''; + } +} + +Gantt.VIEW_MODE = VIEW_MODE; + +function generate_id(task) { + return ( + task.name + + '_' + + Math.random() + .toString(36) + .slice(2, 12) + ); +} + +return Gantt; + +}()); diff --git a/static/js/gantt/frappe-gantt.js.map b/static/js/gantt/frappe-gantt.js.map new file mode 100644 index 0000000..ae8e931 --- /dev/null +++ b/static/js/gantt/frappe-gantt.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 40f9d74a4b17f4d126f3","webpack:///./src/Gantt.js","webpack:///./src/gantt.scss?b27d","webpack:///./src/gantt.scss","webpack:///./~/css-loader/lib/css-base.js","webpack:///./~/style-loader/addStyles.js","webpack:///./src/Bar.js","webpack:///./src/Arrow.js","webpack:///./~/deepmerge/dist/umd.js"],"names":["Gantt","element","tasks","config","self","init","set_defaults","change_view_mode","unselect_all","view_is","get_bar","trigger_event","refresh","view_mode","merge","require","defaults","header_height","column_width","step","view_modes","bar","height","corner_radius","arrow","curve","padding","date_format","custom_popup_html","reset_variables","document","querySelector","SVGElement","HTMLElement","TypeError","_tasks","_bars","_arrows","element_groups","updated_tasks","mode","set_scale","prepare","render","prepare_tasks","prepare_dependencies","prepare_dates","prepare_canvas","map","task","i","_start","moment","start","_end","end","diff","_index","startOf","add","clone","invalid","dependencies","deps","split","d","trim","filter","id","generate_id","dependency_map","t","push","gantt_start","gantt_end","set_gantt_dates","setup_dates","canvas","Snap","addClass","clear","setup_groups","make_grid","make_dates","make_bars","make_arrows","map_arrows_on_bars","set_width","set_scroll_position","bind_grid_click","subtract","endOf","dates","cur_date","groups","group","attr","scale","cur_width","node","getBoundingClientRect","width","actual_width","select","parent_element","parentElement","scroll_pos","get_min_date","scrollLeft","reduce","acc","curr","isSameOrBefore","make_grid_background","make_grid_rows","make_grid_header","make_grid_ticks","make_grid_highlights","grid_width","length","grid_height","rect","appendTo","grid","header_width","rows","lines","row_width","row_height","row_y","line","tick_x","tick_y","tick_height","date","tick_class","day","month","path","format","x","y","daysInMonth","get_dates_to_draw","text","lower_x","lower_y","lower_text","upper_text","$upper_text","upper_x","upper_y","getBBox","x2","remove","last_date","get_date_info","date_text","year","base_pos","x_pos","arrows","dependency","get_task","dep","arr","concat","from_task","to_task","click","details","selectAll","forEach","el","removeClass","modes","Array","isArray","find","name","Math","random","toString","slice","event","args","apply","Bar","gt","draw","bind","action_completed","prepare_values","prepare_plugins","compute_x","compute_y","duration","progress_width","progress","custom_class","bar_group","handle_group","plugin","Element","Paper","global","Fragment","prototype","getX","getY","getWidth","getHeight","getEndX","draw_bar","draw_progress_bar","draw_label","draw_resize_handles","$bar","$bar_progress","update_label_position","handle_width","polygon","get_progress_polygon_points","bar_progress","setup_click_event","show_details","bind_resize","bind_drag","bind_resize_progress","popover_group","details_box","render_details","f","shadow","e","get_details_position","transform","html","get_details_html","foreign_object","parse","append","isFunction","start_date","end_date","heading","line_1","line_2","get_handles","left","right","drag","onmove_left","onstart","onstop_left","onmove_right","onstop_right","dx","dy","onmove_handle_right","onstop_handle_right","onmove_handle_left","onstop_handle_left","onmove","onstop","handle","on_move","on_start","on_stop","max_dx","min_dx","owidth","finaldx","progress_changed","set_action_completed","ox","oy","run_method_for_dependencies","get_snap_position","update_bar_position","date_changed","fn","dm","deptask","dt","xs","valid_x","prev","update_attr","update_handle_position","update_progressbar_position","update_arrow_position","update_details_position","hasClass","toggleClass","compute_start_end_date","new_start_date","new_end_date","new_progress","compute_progress","setTimeout","x_in_units","width_in_units","parseInt","odx","rem","position","value","isNaN","label","update","functionToCheck","getType","call","Arrow","start_x","condition","start_y","end_x","end_y","from_is_below_to","clockwise","curve_y","offset","down_1","down_2"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;;;;;;mBCzBwBA,K;;AALxB;;AAEA;;;;AACA;;;;;;AAEe,UAASA,KAAT,CAAeC,OAAf,EAAwBC,KAAxB,EAA4C;AAAA,MAAbC,MAAa,uEAAJ,EAAI;;;AAE1D,MAAMC,OAAO,EAAb;;AAEA,WAASC,IAAT,GAAgB;AACfC;;AAEA;AACAF,QAAKG,gBAAL,GAAwBA,gBAAxB;AACAH,QAAKI,YAAL,GAAoBA,YAApB;AACAJ,QAAKK,OAAL,GAAeA,OAAf;AACAL,QAAKM,OAAL,GAAeA,OAAf;AACAN,QAAKO,aAAL,GAAqBA,aAArB;AACAP,QAAKQ,OAAL,GAAeA,OAAf;;AAEA;AACAL,oBAAiBH,KAAKD,MAAL,CAAYU,SAA7B;AACA;;AAED,WAASP,YAAT,GAAwB;;AAEvB,OAAMQ,QAAQ,mBAAAC,CAAQ,CAAR,CAAd;;AAEA,OAAMC,WAAW;AAChBC,mBAAe,EADC;AAEhBC,kBAAc,EAFE;AAGhBC,UAAM,EAHU;AAIhBC,gBAAY,CACX,aADW,EAEX,UAFW,EAGX,KAHW,EAIX,MAJW,EAKX,OALW,CAJI;AAWhBC,SAAK;AACJC,aAAQ,EADJ;AAEJC,oBAAe;AAFX,KAXW;AAehBC,WAAO;AACNC,YAAO;AADD,KAfS;AAkBhBC,aAAS,EAlBO;AAmBhBb,eAAW,KAnBK;AAoBhBc,iBAAa,YApBG;AAqBhBC,uBAAmB;AArBH,IAAjB;AAuBAxB,QAAKD,MAAL,GAAcW,MAAME,QAAN,EAAgBb,MAAhB,CAAd;;AAEA0B,mBAAgB3B,KAAhB;AACA;;AAED,WAAS2B,eAAT,CAAyB3B,KAAzB,EAAgC;AAC/B,OAAG,OAAOD,OAAP,KAAmB,QAAtB,EAAgC;AAC/BG,SAAKH,OAAL,GAAe6B,SAASC,aAAT,CAAuB9B,OAAvB,CAAf;AACA,IAFD,MAEO,IAAIA,mBAAmB+B,UAAvB,EAAmC;AACzC5B,SAAKH,OAAL,GAAeA,OAAf;AACA,IAFM,MAEA,IAAIA,mBAAmBgC,WAAvB,EAAoC;AAC1C7B,SAAKH,OAAL,GAAeA,QAAQ8B,aAAR,CAAsB,KAAtB,CAAf;AACA,IAFM,MAEA;AACN,UAAM,IAAIG,SAAJ,CAAc,+DACnB,oEADK,CAAN;AAEA;;AAED9B,QAAK+B,MAAL,GAAcjC,KAAd;;AAEAE,QAAKgC,KAAL,GAAa,EAAb;AACAhC,QAAKiC,OAAL,GAAe,EAAf;AACAjC,QAAKkC,cAAL,GAAsB,EAAtB;AACA;;AAED,WAAS1B,OAAT,CAAiB2B,aAAjB,EAAgC;AAC/BV,mBAAgBU,aAAhB;AACAhC,oBAAiBH,KAAKD,MAAL,CAAYU,SAA7B;AACA;;AAED,WAASN,gBAAT,CAA0BiC,IAA1B,EAAgC;AAC/BC,aAAUD,IAAV;AACAE;AACAC;AACA;AACAhC,iBAAc,aAAd,EAA6B,CAAC6B,IAAD,CAA7B;AACA;;AAED,WAASE,OAAT,GAAmB;AAClBE;AACAC;AACAC;AACAC;AACA;;AAED,WAASH,aAAT,GAAyB;;AAExB;AACAxC,QAAKF,KAAL,GAAaE,KAAK+B,MAAL,CAAYa,GAAZ,CAAgB,UAACC,IAAD,EAAOC,CAAP,EAAa;;AAEzC;AACAD,SAAKE,MAAL,GAAcC,OAAOH,KAAKI,KAAZ,EAAmBjD,KAAKD,MAAL,CAAYwB,WAA/B,CAAd;AACAsB,SAAKK,IAAL,GAAYF,OAAOH,KAAKM,GAAZ,EAAiBnD,KAAKD,MAAL,CAAYwB,WAA7B,CAAZ;;AAEA;AACA,QAAGsB,KAAKK,IAAL,CAAUE,IAAV,CAAeP,KAAKE,MAApB,EAA4B,OAA5B,IAAuC,EAA1C,EAA8C;AAC7CF,UAAKM,GAAL,GAAW,IAAX;AACA;;AAED;AACAN,SAAKQ,MAAL,GAAcP,CAAd;;AAEA;AACA,QAAG,CAACD,KAAKI,KAAN,IAAe,CAACJ,KAAKM,GAAxB,EAA6B;AAC5BN,UAAKE,MAAL,GAAcC,SAASM,OAAT,CAAiB,KAAjB,CAAd;AACAT,UAAKK,IAAL,GAAYF,SAASM,OAAT,CAAiB,KAAjB,EAAwBC,GAAxB,CAA4B,CAA5B,EAA+B,MAA/B,CAAZ;AACA;AACD,QAAG,CAACV,KAAKI,KAAN,IAAeJ,KAAKM,GAAvB,EAA4B;AAC3BN,UAAKE,MAAL,GAAcF,KAAKK,IAAL,CAAUM,KAAV,GAAkBD,GAAlB,CAAsB,CAAC,CAAvB,EAA0B,MAA1B,CAAd;AACA;AACD,QAAGV,KAAKI,KAAL,IAAc,CAACJ,KAAKM,GAAvB,EAA4B;AAC3BN,UAAKK,IAAL,GAAYL,KAAKE,MAAL,CAAYS,KAAZ,GAAoBD,GAApB,CAAwB,CAAxB,EAA2B,MAA3B,CAAZ;AACA;;AAED;AACA,QAAG,CAACV,KAAKI,KAAN,IAAe,CAACJ,KAAKM,GAAxB,EAA6B;AAC5BN,UAAKY,OAAL,GAAe,IAAf;AACA;;AAED;AACA,QAAG,OAAOZ,KAAKa,YAAZ,KAA6B,QAA7B,IAAyC,CAACb,KAAKa,YAAlD,EAAgE;AAC/D,SAAIC,OAAO,EAAX;AACA,SAAGd,KAAKa,YAAR,EAAsB;AACrBC,aAAOd,KAAKa,YAAL,CACLE,KADK,CACC,GADD,EAELhB,GAFK,CAED;AAAA,cAAKiB,EAAEC,IAAF,EAAL;AAAA,OAFC,EAGLC,MAHK,CAGE,UAACF,CAAD;AAAA,cAAOA,CAAP;AAAA,OAHF,CAAP;AAIA;AACDhB,UAAKa,YAAL,GAAoBC,IAApB;AACA;;AAED;AACA,QAAG,CAACd,KAAKmB,EAAT,EAAa;AACZnB,UAAKmB,EAAL,GAAUC,YAAYpB,IAAZ,CAAV;AACA;;AAED,WAAOA,IAAP;AACA,IAjDY,CAAb;AAkDA;;AAED,WAASJ,oBAAT,GAAgC;;AAE/BzC,QAAKkE,cAAL,GAAsB,EAAtB;AAF+B;AAAA;AAAA;;AAAA;AAG/B,yBAAalE,KAAKF,KAAlB,8HAAyB;AAAA,SAAjBqE,CAAiB;AAAA;AAAA;AAAA;;AAAA;AACxB,4BAAaA,EAAET,YAAf,mIAA6B;AAAA,WAArBG,CAAqB;;AAC5B7D,YAAKkE,cAAL,CAAoBL,CAApB,IAAyB7D,KAAKkE,cAAL,CAAoBL,CAApB,KAA0B,EAAnD;AACA7D,YAAKkE,cAAL,CAAoBL,CAApB,EAAuBO,IAAvB,CAA4BD,EAAEH,EAA9B;AACA;AAJuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKxB;AAR8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS/B;;AAED,WAAStB,aAAT,GAAyB;;AAExB1C,QAAKqE,WAAL,GAAmBrE,KAAKsE,SAAL,GAAiB,IAApC;AAFwB;AAAA;AAAA;;AAAA;AAGxB,0BAAgBtE,KAAKF,KAArB,mIAA4B;AAAA,SAApB+C,IAAoB;;AAC3B;AACA,SAAG,CAAC7C,KAAKqE,WAAN,IAAqBxB,KAAKE,MAAL,GAAc/C,KAAKqE,WAA3C,EAAwD;AACvDrE,WAAKqE,WAAL,GAAmBxB,KAAKE,MAAxB;AACA;AACD,SAAG,CAAC/C,KAAKsE,SAAN,IAAmBzB,KAAKK,IAAL,GAAYlD,KAAKsE,SAAvC,EAAkD;AACjDtE,WAAKsE,SAAL,GAAiBzB,KAAKK,IAAtB;AACA;AACD;AAXuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAYxBqB;AACAC;AACA;;AAED,WAAS7B,cAAT,GAA0B;AACzB,OAAG3C,KAAKyE,MAAR,EAAgB;AAChBzE,QAAKyE,MAAL,GAAcC,KAAK1E,KAAKH,OAAV,EAAmB8E,QAAnB,CAA4B,OAA5B,CAAd;AACA;;AAED,WAASpC,MAAT,GAAkB;AACjBqC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACA;;AAED,WAAST,KAAT,GAAiB;AAChB5E,QAAKyE,MAAL,CAAYG,KAAZ;AACA5E,QAAKgC,KAAL,GAAa,EAAb;AACAhC,QAAKiC,OAAL,GAAe,EAAf;AACA;;AAED,WAASsC,eAAT,GAA2B;;AAE1B,OAAGlE,QAAQ,CAAC,aAAD,EAAgB,UAAhB,CAAR,CAAH,EAAyC;AACxCL,SAAKqE,WAAL,GAAmBrE,KAAKqE,WAAL,CAAiBb,KAAjB,GAAyB8B,QAAzB,CAAkC,CAAlC,EAAqC,KAArC,CAAnB;AACAtF,SAAKsE,SAAL,GAAiBtE,KAAKsE,SAAL,CAAed,KAAf,GAAuBD,GAAvB,CAA2B,CAA3B,EAA8B,KAA9B,CAAjB;AACA,IAHD,MAGO,IAAGlD,QAAQ,OAAR,CAAH,EAAqB;AAC3BL,SAAKqE,WAAL,GAAmBrE,KAAKqE,WAAL,CAAiBb,KAAjB,GAAyBF,OAAzB,CAAiC,MAAjC,CAAnB;AACAtD,SAAKsE,SAAL,GAAiBtE,KAAKsE,SAAL,CAAed,KAAf,GAAuB+B,KAAvB,CAA6B,OAA7B,EAAsChC,GAAtC,CAA0C,CAA1C,EAA6C,MAA7C,CAAjB;AACA,IAHM,MAGA;AACNvD,SAAKqE,WAAL,GAAmBrE,KAAKqE,WAAL,CAAiBb,KAAjB,GAAyBF,OAAzB,CAAiC,OAAjC,EAA0CgC,QAA1C,CAAmD,CAAnD,EAAsD,OAAtD,CAAnB;AACAtF,SAAKsE,SAAL,GAAiBtE,KAAKsE,SAAL,CAAed,KAAf,GAAuB+B,KAAvB,CAA6B,OAA7B,EAAsChC,GAAtC,CAA0C,CAA1C,EAA6C,OAA7C,CAAjB;AACA;AACD;;AAED,WAASiB,WAAT,GAAuB;;AAEtBxE,QAAKwF,KAAL,GAAa,EAAb;AACA,OAAIC,WAAW,IAAf;;AAEA,UAAMA,aAAa,IAAb,IAAqBA,WAAWzF,KAAKsE,SAA3C,EAAsD;AACrD,QAAG,CAACmB,QAAJ,EAAc;AACbA,gBAAWzF,KAAKqE,WAAL,CAAiBb,KAAjB,EAAX;AACA,KAFD,MAEO;AACNiC,gBAAWpF,QAAQ,OAAR,IACVoF,SAASjC,KAAT,GAAiBD,GAAjB,CAAqB,CAArB,EAAwB,OAAxB,CADU,GAEVkC,SAASjC,KAAT,GAAiBD,GAAjB,CAAqBvD,KAAKD,MAAL,CAAYgB,IAAjC,EAAuC,OAAvC,CAFD;AAGA;AACDf,SAAKwF,KAAL,CAAWpB,IAAX,CAAgBqB,QAAhB;AACA;AACD;;AAED,WAASZ,YAAT,GAAwB;;AAEvB,OAAMa,SAAS,CAAC,MAAD,EAAS,MAAT,EAAiB,OAAjB,EAA0B,UAA1B,EAAsC,KAAtC,EAA6C,SAA7C,CAAf;AACA;AAHuB;AAAA;AAAA;;AAAA;AAIvB,0BAAiBA,MAAjB,mIAAyB;AAAA,SAAjBC,KAAiB;;AACxB3F,UAAKkC,cAAL,CAAoByD,KAApB,IAA6B3F,KAAKyE,MAAL,CAAYkB,KAAZ,GAAoBC,IAApB,CAAyB,EAAC,MAAMD,KAAP,EAAzB,CAA7B;AACA;AANsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOvB;;AAED,WAAStD,SAAT,CAAmBwD,KAAnB,EAA0B;AACzB7F,QAAKD,MAAL,CAAYU,SAAZ,GAAwBoF,KAAxB;;AAEA,OAAGA,UAAU,KAAb,EAAoB;AACnB7F,SAAKD,MAAL,CAAYgB,IAAZ,GAAmB,EAAnB;AACAf,SAAKD,MAAL,CAAYe,YAAZ,GAA2B,EAA3B;AACA,IAHD,MAGO,IAAG+E,UAAU,UAAb,EAAyB;AAC/B7F,SAAKD,MAAL,CAAYgB,IAAZ,GAAmB,KAAK,CAAxB;AACAf,SAAKD,MAAL,CAAYe,YAAZ,GAA2B,EAA3B;AACA,IAHM,MAGA,IAAG+E,UAAU,aAAb,EAA4B;AAClC7F,SAAKD,MAAL,CAAYgB,IAAZ,GAAmB,KAAK,CAAxB;AACAf,SAAKD,MAAL,CAAYe,YAAZ,GAA2B,EAA3B;AACA,IAHM,MAGA,IAAG+E,UAAU,MAAb,EAAqB;AAC3B7F,SAAKD,MAAL,CAAYgB,IAAZ,GAAmB,KAAK,CAAxB;AACAf,SAAKD,MAAL,CAAYe,YAAZ,GAA2B,GAA3B;AACA,IAHM,MAGA,IAAG+E,UAAU,OAAb,EAAsB;AAC5B7F,SAAKD,MAAL,CAAYgB,IAAZ,GAAmB,KAAK,EAAxB;AACAf,SAAKD,MAAL,CAAYe,YAAZ,GAA2B,GAA3B;AACA;AACD;;AAED,WAASqE,SAAT,GAAqB;AACpB,OAAMW,YAAY9F,KAAKyE,MAAL,CAAYsB,IAAZ,CAAiBC,qBAAjB,GAAyCC,KAA3D;AACA,OAAMC,eAAelG,KAAKyE,MAAL,CAAY0B,MAAZ,CAAmB,iBAAnB,EAAsCP,IAAtC,CAA2C,OAA3C,CAArB;AACA,OAAGE,YAAYI,YAAf,EAA6B;AAC5BlG,SAAKyE,MAAL,CAAYmB,IAAZ,CAAiB,OAAjB,EAA0BM,YAA1B;AACA;AACD;;AAED,WAASd,mBAAT,GAA+B;AAC9B,OAAMgB,iBAAiBpG,KAAKH,OAAL,CAAawG,aAApC;;AAEA,OAAG,CAACD,cAAJ,EAAoB;;AAEpB,OAAME,aAAaC,eAAenD,IAAf,CAAoBpD,KAAKqE,WAAzB,EAAsC,OAAtC,IAClBrE,KAAKD,MAAL,CAAYgB,IADM,GACCf,KAAKD,MAAL,CAAYe,YADb,GAC4Bd,KAAKD,MAAL,CAAYe,YAD3D;AAEAsF,kBAAeI,UAAf,GAA4BF,UAA5B;AACA;;AAED,WAASC,YAAT,GAAwB;AACvB,OAAM1D,OAAO7C,KAAKF,KAAL,CAAW2G,MAAX,CAAkB,UAACC,GAAD,EAAMC,IAAN,EAAe;AAC7C,WAAOA,KAAK5D,MAAL,CAAY6D,cAAZ,CAA2BF,IAAI3D,MAA/B,IAAyC4D,IAAzC,GAAgDD,GAAvD;AACA,IAFY,CAAb;AAGA,UAAO7D,KAAKE,MAAZ;AACA;;AAED,WAAS+B,SAAT,GAAqB;AACpB+B;AACAC;AACAC;AACAC;AACAC;AACA;;AAED,WAASJ,oBAAT,GAAgC;;AAE/B,OAAMK,aAAalH,KAAKwF,KAAL,CAAW2B,MAAX,GAAoBnH,KAAKD,MAAL,CAAYe,YAAnD;AAAA,OACCsG,cAAcpH,KAAKD,MAAL,CAAYc,aAAZ,GAA4Bb,KAAKD,MAAL,CAAYuB,OAAxC,GACb,CAACtB,KAAKD,MAAL,CAAYkB,GAAZ,CAAgBC,MAAhB,GAAyBlB,KAAKD,MAAL,CAAYuB,OAAtC,IAAiDtB,KAAKF,KAAL,CAAWqH,MAF9D;;AAIAnH,QAAKyE,MAAL,CAAY4C,IAAZ,CAAiB,CAAjB,EAAoB,CAApB,EAAuBH,UAAvB,EAAmCE,WAAnC,EACEzC,QADF,CACW,iBADX,EAEE2C,QAFF,CAEWtH,KAAKkC,cAAL,CAAoBqF,IAF/B;;AAIAvH,QAAKyE,MAAL,CAAYmB,IAAZ,CAAiB;AAChB1E,YAAQkG,cAAcpH,KAAKD,MAAL,CAAYuB,OAA1B,GAAoC,GAD5B;AAEhB2E,WAAO;AAFS,IAAjB;AAIA;;AAED,WAASc,gBAAT,GAA4B;AAC3B,OAAMS,eAAexH,KAAKwF,KAAL,CAAW2B,MAAX,GAAoBnH,KAAKD,MAAL,CAAYe,YAArD;AAAA,OACCD,gBAAgBb,KAAKD,MAAL,CAAYc,aAAZ,GAA4B,EAD7C;AAEAb,QAAKyE,MAAL,CAAY4C,IAAZ,CAAiB,CAAjB,EAAoB,CAApB,EAAuBG,YAAvB,EAAqC3G,aAArC,EACE8D,QADF,CACW,aADX,EAEE2C,QAFF,CAEWtH,KAAKkC,cAAL,CAAoBqF,IAF/B;AAGA;;AAED,WAAST,cAAT,GAA0B;;AAEzB,OAAMW,OAAOzH,KAAKyE,MAAL,CAAYkB,KAAZ,GAAoB2B,QAApB,CAA6BtH,KAAKkC,cAAL,CAAoBqF,IAAjD,CAAb;AAAA,OACCG,QAAQ1H,KAAKyE,MAAL,CAAYkB,KAAZ,GAAoB2B,QAApB,CAA6BtH,KAAKkC,cAAL,CAAoBqF,IAAjD,CADT;AAAA,OAECI,YAAY3H,KAAKwF,KAAL,CAAW2B,MAAX,GAAoBnH,KAAKD,MAAL,CAAYe,YAF7C;AAAA,OAGC8G,aAAa5H,KAAKD,MAAL,CAAYkB,GAAZ,CAAgBC,MAAhB,GAAyBlB,KAAKD,MAAL,CAAYuB,OAHnD;;AAKA,OAAIuG,QAAQ7H,KAAKD,MAAL,CAAYc,aAAZ,GAA4Bb,KAAKD,MAAL,CAAYuB,OAAZ,GAAsB,CAA9D;;AAPyB;AAAA;AAAA;;AAAA;AASzB,0BAAgBtB,KAAKF,KAArB,mIAA4B;AAAA,SAApB+C,IAAoB;AAAE;AAC7B7C,UAAKyE,MAAL,CAAY4C,IAAZ,CAAiB,CAAjB,EAAoBQ,KAApB,EAA2BF,SAA3B,EAAsCC,UAAtC,EACEjD,QADF,CACW,UADX,EAEE2C,QAFF,CAEWG,IAFX;;AAIAzH,UAAKyE,MAAL,CAAYqD,IAAZ,CAAiB,CAAjB,EAAoBD,QAAQD,UAA5B,EAAwCD,SAAxC,EAAmDE,QAAQD,UAA3D,EACEjD,QADF,CACW,UADX,EAEE2C,QAFF,CAEWI,KAFX;;AAIAG,cAAS7H,KAAKD,MAAL,CAAYkB,GAAZ,CAAgBC,MAAhB,GAAyBlB,KAAKD,MAAL,CAAYuB,OAA9C;AACA;AAnBwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBzB;;AAED,WAAS0F,eAAT,GAA2B;AAC1B,OAAIe,SAAS,CAAb;AAAA,OACCC,SAAShI,KAAKD,MAAL,CAAYc,aAAZ,GAA4Bb,KAAKD,MAAL,CAAYuB,OAAZ,GAAsB,CAD5D;AAAA,OAEC2G,cAAc,CAACjI,KAAKD,MAAL,CAAYkB,GAAZ,CAAgBC,MAAhB,GAAyBlB,KAAKD,MAAL,CAAYuB,OAAtC,IAAiDtB,KAAKF,KAAL,CAAWqH,MAF3E;;AAD0B;AAAA;AAAA;;AAAA;AAK1B,0BAAgBnH,KAAKwF,KAArB,mIAA4B;AAAA,SAApB0C,IAAoB;;AAC3B,SAAIC,aAAa,MAAjB;AACA;AACA,SAAG9H,QAAQ,KAAR,KAAkB6H,KAAKE,GAAL,OAAe,CAApC,EAAuC;AACtCD,oBAAc,QAAd;AACA;AACD;AACA,SAAG9H,QAAQ,MAAR,KAAmB6H,KAAKA,IAAL,MAAe,CAAlC,IAAuCA,KAAKA,IAAL,KAAc,CAAxD,EAA2D;AAC1DC,oBAAc,QAAd;AACA;AACD;AACA,SAAG9H,QAAQ,OAAR,KAAoB6H,KAAKG,KAAL,KAAe,CAAf,KAAqB,CAA5C,EAA+C;AAC9CF,oBAAc,QAAd;AACA;;AAEDnI,UAAKyE,MAAL,CAAY6D,IAAZ,CAAiB5D,KAAK6D,MAAL,CAAY,sBAAZ,EAAoC;AACpDC,SAAGT,MADiD;AAEpDU,SAAGT,MAFiD;AAGpD9G,cAAQ+G;AAH4C,MAApC,CAAjB,EAKCtD,QALD,CAKUwD,UALV,EAMCb,QAND,CAMUtH,KAAKkC,cAAL,CAAoBqF,IAN9B;;AAQA,SAAGlH,QAAQ,OAAR,CAAH,EAAqB;AACpB0H,gBAAUG,KAAKQ,WAAL,KAAqB1I,KAAKD,MAAL,CAAYe,YAAjC,GAAgD,EAA1D;AACA,MAFD,MAEO;AACNiH,gBAAU/H,KAAKD,MAAL,CAAYe,YAAtB;AACA;AACD;AAjCyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkC1B;;AAED,WAASmG,oBAAT,GAAgC;;AAE/B;AACA,OAAG5G,QAAQ,KAAR,CAAH,EAAmB;AAClB,QAAMmI,IAAIxF,SAASM,OAAT,CAAiB,KAAjB,EAAwBF,IAAxB,CAA6BpD,KAAKqE,WAAlC,EAA+C,OAA/C,IACRrE,KAAKD,MAAL,CAAYgB,IADJ,GACWf,KAAKD,MAAL,CAAYe,YADjC;AAEA,QAAM2H,IAAI,CAAV;AACA,QAAMxC,QAAQjG,KAAKD,MAAL,CAAYe,YAA1B;AACA,QAAMI,SAAS,CAAClB,KAAKD,MAAL,CAAYkB,GAAZ,CAAgBC,MAAhB,GAAyBlB,KAAKD,MAAL,CAAYuB,OAAtC,IAAiDtB,KAAKF,KAAL,CAAWqH,MAA5D,GACdnH,KAAKD,MAAL,CAAYc,aADE,GACcb,KAAKD,MAAL,CAAYuB,OAAZ,GAAsB,CADnD;;AAGAtB,SAAKyE,MAAL,CAAY4C,IAAZ,CAAiBmB,CAAjB,EAAoBC,CAApB,EAAuBxC,KAAvB,EAA8B/E,MAA9B,EACEyD,QADF,CACW,iBADX,EAEE2C,QAFF,CAEWtH,KAAKkC,cAAL,CAAoBqF,IAF/B;AAGA;AACD;;AAED,WAASxC,UAAT,GAAsB;AAAA;AAAA;AAAA;;AAAA;;AAErB,0BAAgB4D,mBAAhB,mIAAqC;AAAA,SAA7BT,IAA6B;;AACpClI,UAAKyE,MAAL,CAAYmE,IAAZ,CAAiBV,KAAKW,OAAtB,EAA+BX,KAAKY,OAApC,EAA6CZ,KAAKa,UAAlD,EACEpE,QADF,CACW,YADX,EAEE2C,QAFF,CAEWtH,KAAKkC,cAAL,CAAoBgG,IAF/B;;AAIA,SAAGA,KAAKc,UAAR,EAAoB;AACnB,UAAMC,cAAcjJ,KAAKyE,MAAL,CAAYmE,IAAZ,CAAiBV,KAAKgB,OAAtB,EAA+BhB,KAAKiB,OAApC,EAA6CjB,KAAKc,UAAlD,EAClBrE,QADkB,CACT,YADS,EAElB2C,QAFkB,CAETtH,KAAKkC,cAAL,CAAoBgG,IAFX,CAApB;;AAIA;AACA,UAAGe,YAAYG,OAAZ,GAAsBC,EAAtB,GAA2BrJ,KAAKkC,cAAL,CAAoBqF,IAApB,CAAyB6B,OAAzB,GAAmCnD,KAAjE,EAAwE;AACvEgD,mBAAYK,MAAZ;AACA;AACD;AACD;AAjBoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBrB;;AAED,WAASX,iBAAT,GAA6B;AAC5B,OAAIY,YAAY,IAAhB;AACA,OAAM/D,QAAQxF,KAAKwF,KAAL,CAAW5C,GAAX,CAAe,UAACsF,IAAD,EAAOpF,CAAP,EAAa;AACzC,QAAMe,IAAI2F,cAActB,IAAd,EAAoBqB,SAApB,EAA+BzG,CAA/B,CAAV;AACAyG,gBAAYrB,IAAZ;AACA,WAAOrE,CAAP;AACA,IAJa,CAAd;AAKA,UAAO2B,KAAP;AACA;;AAED,WAASgE,aAAT,CAAuBtB,IAAvB,EAA6BqB,SAA7B,EAAwCzG,CAAxC,EAA2C;AAC1C,OAAG,CAACyG,SAAJ,EAAe;AACdA,gBAAYrB,KAAK1E,KAAL,GAAaD,GAAb,CAAiB,CAAjB,EAAoB,MAApB,CAAZ;AACA;AACD,OAAMkG,YAAY;AACjB,yBAAqBvB,KAAKK,MAAL,CAAY,IAAZ,CADJ;AAEjB,sBAAkBL,KAAKK,MAAL,CAAY,IAAZ,CAFD;AAGjB,iBAAaL,KAAKA,IAAL,OAAgBqB,UAAUrB,IAAV,EAAhB,GAAmCA,KAAKK,MAAL,CAAY,GAAZ,CAAnC,GAAsD,EAHlD;AAIjB,kBAAcL,KAAKG,KAAL,OAAiBkB,UAAUlB,KAAV,EAAjB,GACbH,KAAKK,MAAL,CAAY,OAAZ,CADa,GACUL,KAAKK,MAAL,CAAY,GAAZ,CALP;AAMjB,mBAAeL,KAAKK,MAAL,CAAY,MAAZ,CANE;AAOjB,yBAAqBL,KAAKA,IAAL,OAAgBqB,UAAUrB,IAAV,EAAhB,GAAmCA,KAAKK,MAAL,CAAY,OAAZ,CAAnC,GAA0D,EAP9D;AAQjB,sBAAkBL,KAAKA,IAAL,OAAgBqB,UAAUrB,IAAV,EAAhB,GACjBA,KAAKG,KAAL,OAAiBkB,UAAUlB,KAAV,EAAjB,GACAH,KAAKK,MAAL,CAAY,OAAZ,CADA,GACuBL,KAAKK,MAAL,CAAY,GAAZ,CAFN,GAEyB,EAV1B;AAWjB,iBAAaL,KAAKG,KAAL,OAAiBkB,UAAUlB,KAAV,EAAjB,GAAqCH,KAAKK,MAAL,CAAY,MAAZ,CAArC,GAA2D,EAXvD;AAYjB,kBAAcL,KAAKG,KAAL,OAAiBkB,UAAUlB,KAAV,EAAjB,GAAqCH,KAAKK,MAAL,CAAY,MAAZ,CAArC,GAA2D,EAZxD;AAajB,mBAAeL,KAAKwB,IAAL,OAAgBH,UAAUG,IAAV,EAAhB,GAAmCxB,KAAKK,MAAL,CAAY,MAAZ,CAAnC,GAAyD;AAbvD,IAAlB;;AAgBA,OAAMoB,WAAW;AAChBnB,OAAG1F,IAAI9C,KAAKD,MAAL,CAAYe,YADH;AAEhBgI,aAAS9I,KAAKD,MAAL,CAAYc,aAFL;AAGhBsI,aAASnJ,KAAKD,MAAL,CAAYc,aAAZ,GAA4B;AAHrB,IAAjB;;AAMA,OAAM+I,QAAQ;AACb,yBAAsB5J,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,CAA5B,GAAiC,CADzC;AAEb,yBAAqB,CAFR;AAGb,sBAAmBd,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,CAA5B,GAAiC,CAHtC;AAIb,sBAAkB,CAJL;AAKb,iBAAad,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,CAL3B;AAMb,iBAAcd,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,EAA5B,GAAkC,CANlC;AAOb,kBAAc,CAPD;AAQb,kBAAed,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,CAA5B,GAAiC,CARlC;AASb,mBAAed,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,CAT7B;AAUb,mBAAgBd,KAAKD,MAAL,CAAYe,YAAZ,GAA2B,EAA5B,GAAkC;AAVpC,IAAd;;AAaA,UAAO;AACNkI,gBAAYS,UAAazJ,KAAKD,MAAL,CAAYU,SAAzB,YADN;AAENsI,gBAAYU,UAAazJ,KAAKD,MAAL,CAAYU,SAAzB,YAFN;AAGNyI,aAASS,SAASnB,CAAT,GAAaoB,MAAS5J,KAAKD,MAAL,CAAYU,SAArB,YAHhB;AAIN0I,aAASQ,SAASR,OAJZ;AAKNN,aAASc,SAASnB,CAAT,GAAaoB,MAAS5J,KAAKD,MAAL,CAAYU,SAArB,YALhB;AAMNqI,aAASa,SAASb;AANZ,IAAP;AAQA;;AAED,WAAS7D,WAAT,GAAuB;AACtBjF,QAAKiC,OAAL,GAAe,EAAf;AADsB;AAAA;AAAA;;AAAA;AAAA;AAAA,SAEdY,IAFc;;AAGrB,SAAIgH,SAAS,EAAb;AACAA,cAAShH,KAAKa,YAAL,CAAkBd,GAAlB,CAAsB,eAAO;AACrC,UAAMkH,aAAaC,SAASC,GAAT,CAAnB;AACA,UAAG,CAACF,UAAJ,EAAgB;;AAEhB,UAAM1I,QAAQ,qBACbpB,IADa,EACP;AACNA,WAAKgC,KAAL,CAAW8H,WAAWzG,MAAtB,CAFa,EAEkB;AAC/BrD,WAAKgC,KAAL,CAAWa,KAAKQ,MAAhB,CAHa,CAGW;AAHX,OAAd;AAKArD,WAAKkC,cAAL,CAAoBd,KAApB,CAA0BmC,GAA1B,CAA8BnC,MAAMvB,OAApC;AACA,aAAOuB,KAAP,CAVqC,CAUvB;AACd,MAXQ,EAWN2C,MAXM,CAWC;AAAA,aAAOkG,GAAP;AAAA,MAXD,CAAT,CAJqB,CAeE;AACvBjK,UAAKiC,OAAL,GAAejC,KAAKiC,OAAL,CAAaiI,MAAb,CAAoBL,MAApB,CAAf;AAhBqB;;AAEtB,0BAAgB7J,KAAKF,KAArB,mIAA4B;AAAA;AAe3B;AAjBqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBtB;;AAED,WAASkF,SAAT,GAAqB;;AAEpBhF,QAAKgC,KAAL,GAAahC,KAAKF,KAAL,CAAW8C,GAAX,CAAe,UAACC,IAAD,EAAU;AACrC,QAAM5B,MAAM,mBAAIjB,IAAJ,EAAU6C,IAAV,CAAZ;AACA7C,SAAKkC,cAAL,CAAoBjB,GAApB,CAAwBsC,GAAxB,CAA4BtC,IAAI0E,KAAhC;AACA,WAAO1E,GAAP;AACA,IAJY,CAAb;AAKA;;AAED,WAASiE,kBAAT,GAA8B;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,SACrBjE,GADqB;;AAE5BA,SAAI4I,MAAJ,GAAa7J,KAAKiC,OAAL,CAAa8B,MAAb,CAAoB,iBAAS;AACzC,aAAQ3C,MAAM+I,SAAN,CAAgBtH,IAAhB,CAAqBmB,EAArB,KAA4B/C,IAAI4B,IAAJ,CAASmB,EAAtC,IACL5C,MAAMgJ,OAAN,CAAcvH,IAAd,CAAmBmB,EAAnB,KAA0B/C,IAAI4B,IAAJ,CAASmB,EADrC;AAEA,MAHY,CAAb;AAF4B;;AAC7B,0BAAehE,KAAKgC,KAApB,mIAA2B;AAAA;AAK1B;AAN4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAO7B;;AAED,WAASqD,eAAT,GAA2B;AAC1BrF,QAAKkC,cAAL,CAAoBqF,IAApB,CAAyB8C,KAAzB,CAA+B,YAAM;AACpCjK;AACAJ,SAAKkC,cAAL,CAAoBoI,OAApB,CACEC,SADF,CACY,kBADZ,EAEEC,OAFF,CAEU;AAAA,YAAMC,GAAG9F,QAAH,CAAY,MAAZ,CAAN;AAAA,KAFV;AAGA,IALD;AAMA;;AAED,WAASvE,YAAT,GAAwB;AACvBJ,QAAKyE,MAAL,CAAY8F,SAAZ,CAAsB,cAAtB,EAAsCC,OAAtC,CAA8C,cAAM;AACnDC,OAAGC,WAAH,CAAe,QAAf;AACA,IAFD;AAGA;;AAED,WAASrK,OAAT,CAAiBsK,KAAjB,EAAwB;AACvB,OAAI,OAAOA,KAAP,KAAiB,QAArB,EAA+B;AAC9B,WAAO3K,KAAKD,MAAL,CAAYU,SAAZ,KAA0BkK,KAAjC;AACA,IAFD,MAEO,IAAGC,MAAMC,OAAN,CAAcF,KAAd,CAAH,EAAyB;AAAA;AAAA;AAAA;;AAAA;AAC/B,4BAAiBA,KAAjB,wIAAwB;AAAA,UAAfvI,IAAe;;AACvB,UAAGpC,KAAKD,MAAL,CAAYU,SAAZ,KAA0B2B,IAA7B,EAAmC,OAAO,IAAP;AACnC;AAH8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAI/B,WAAO,KAAP;AACA;AACD;;AAED,WAAS2H,QAAT,CAAkB/F,EAAlB,EAAsB;AACrB,UAAOhE,KAAKF,KAAL,CAAWgL,IAAX,CAAgB,UAACjI,IAAD,EAAU;AAChC,WAAOA,KAAKmB,EAAL,KAAYA,EAAnB;AACA,IAFM,CAAP;AAGA;;AAED,WAAS1D,OAAT,CAAiB0D,EAAjB,EAAqB;AACpB,UAAOhE,KAAKgC,KAAL,CAAW8I,IAAX,CAAgB,UAAC7J,GAAD,EAAS;AAC/B,WAAOA,IAAI4B,IAAJ,CAASmB,EAAT,KAAgBA,EAAvB;AACA,IAFM,CAAP;AAGA;;AAED,WAASC,WAAT,CAAqBpB,IAArB,EAA2B;AAC1B,UAAOA,KAAKkI,IAAL,GAAY,GAAZ,GAAkBC,KAAKC,MAAL,GAAcC,QAAd,CAAuB,EAAvB,EAA2BC,KAA3B,CAAiC,CAAjC,EAAoC,EAApC,CAAzB;AACA;;AAED,WAAS5K,aAAT,CAAuB6K,KAAvB,EAA8BC,IAA9B,EAAoC;AACnC,OAAGrL,KAAKD,MAAL,CAAY,QAAQqL,KAApB,CAAH,EAA+B;AAC9BpL,SAAKD,MAAL,CAAY,QAAQqL,KAApB,EAA2BE,KAA3B,CAAiC,IAAjC,EAAuCD,IAAvC;AACA;AACD;;AAEDpL;;AAEA,SAAOD,IAAP;AACA,E,CA1jBD;AACA;;;;;;;;;;;;;ACDA;;AAEA;AACA;AACA;AACA;AACA,gDAA8E;AAC9E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAG;AACH;AACA;AACA,iCAAgC,UAAU,EAAE;AAC5C,E;;;;;;ACpBA;AACA;;;AAGA;AACA,oDAAmD,eAAe,EAAE,yBAAyB,kBAAkB,oBAAoB,sBAAsB,EAAE,sBAAsB,kBAAkB,EAAE,sCAAsC,kBAAkB,EAAE,sBAAsB,oBAAoB,EAAE,kBAAkB,oBAAoB,sBAAsB,EAAE,wBAAwB,wBAAwB,EAAE,6BAA6B,kBAAkB,iBAAiB,EAAE,mBAAmB,eAAe,iBAAiB,sBAAsB,EAAE,iBAAiB,kBAAkB,oBAAoB,oBAAoB,sCAAsC,EAAE,0BAA0B,kBAAkB,EAAE,yBAAyB,sBAAsB,oBAAoB,oBAAoB,wBAAwB,EAAE,sCAAsC,iBAAiB,EAAE,uBAAuB,eAAe,+BAA+B,wBAAwB,oBAAoB,yBAAyB,EAAE,2BAA2B,iBAAiB,yBAAyB,EAAE,oBAAoB,eAAe,sBAAsB,eAAe,uBAAuB,iCAAiC,EAAE,yBAAyB,oBAAoB,EAAE,oCAAoC,sBAAsB,EAAE,uCAAuC,0BAA0B,iBAAiB,EAAE,qCAAqC,sBAAsB,EAAE,4CAA4C,oBAAoB,wBAAwB,EAAE,wBAAwB,eAAe,EAAE,wBAAwB,eAAe,EAAE,wCAAwC,qBAAqB,0BAA0B,kBAAkB,EAAE,iFAAiF,gBAAgB,EAAE,2CAA2C,sBAAsB,wBAAwB,0BAA0B,kBAAkB,EAAE,0CAA0C,sBAAsB,yBAAyB,kBAAkB,EAAE,qDAAqD,uBAAuB,EAAE,kBAAkB,kBAAkB,EAAE,UAAU,kGAAkG,gBAAgB,KAAK,UAAU,aAAa,qBAAqB,KAAK,gBAAgB,KAAK,oBAAoB,KAAK,sBAAsB,MAAM,cAAc,oBAAoB,MAAM,oBAAoB,MAAM,aAAa,kBAAkB,MAAM,WAAW,WAAW,oBAAoB,MAAM,aAAa,eAAe,aAAa,mBAAmB,MAAM,mBAAmB,MAAM,aAAa,cAAc,aAAa,kBAAkB,MAAM,mBAAmB,MAAM,WAAW,YAAY,aAAa,YAAY,kBAAkB,MAAM,YAAY,oBAAoB,MAAM,aAAa,cAAc,WAAW,YAAY,oBAAoB,MAAM,kBAAkB,MAAM,iBAAiB,MAAM,aAAa,gBAAgB,MAAM,kBAAkB,MAAM,YAAY,mBAAmB,MAAM,mBAAmB,MAAM,mBAAmB,MAAM,aAAa,aAAa,iBAAiB,MAAM,gBAAgB,MAAM,YAAY,YAAY,aAAa,kBAAkB,MAAM,YAAY,YAAY,kBAAkB,MAAM,oBAAoB,MAAM,4EAA4E,uBAAuB,yBAAyB,qBAAqB,+BAA+B,yBAAyB,oBAAoB,oBAAoB,oBAAoB,iBAAiB,sBAAsB,YAAY,wBAAwB,iBAAiB,KAAK,kBAAkB,oBAAoB,4BAA4B,wBAAwB,KAAK,eAAe,oBAAoB,KAAK,+BAA+B,sBAAsB,KAAK,eAAe,kCAAkC,KAAK,WAAW,4BAA4B,wBAAwB,eAAe,0BAA0B,OAAO,KAAK,sBAAsB,0BAA0B,mBAAmB,KAAK,cAAc,iBAAiB,0BAA0B,wBAAwB,KAAK,YAAY,uBAAuB,0BAA0B,sBAAsB,wCAAwC,KAAK,mBAAmB,kBAAkB,KAAK,kBAAkB,wBAAwB,0BAA0B,sBAAsB,0BAA0B,sBAAsB,0BAA0B,OAAO,KAAK,gBAAgB,iBAAiB,iCAAiC,0BAA0B,sBAAsB,2BAA2B,eAAe,0BAA0B,2BAA2B,OAAO,KAAK,eAAe,0BAA0B,wBAAwB,iBAAiB,yBAAyB,mCAAmC,KAAK,oBAAoB,sBAAsB,iBAAiB,cAAc,0BAA0B,SAAS,mBAAmB,8BAA8B,qBAAqB,SAAS,OAAO,kBAAkB,cAAc,0BAA0B,SAAS,OAAO,KAAK,gCAAgC,sBAAsB,0BAA0B,KAAK,iBAAiB,wBAAwB,KAAK,iBAAiB,wBAAwB,KAAK,mCAAmC,uBAAuB,4BAA4B,oBAAoB,eAAe,kBAAkB,OAAO,YAAY,wBAAwB,0BAA0B,4BAA4B,2BAA2B,OAAO,WAAW,wBAAwB,2BAA2B,2BAA2B,OAAO,sBAAsB,yBAAyB,OAAO,KAAK,aAAa,oBAAoB,KAAK,GAAG,qBAAqB;;AAExxL;;;;;;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,iBAAgB,iBAAiB;AACjC;AACA;AACA,yCAAwC,gBAAgB;AACxD,KAAI;AACJ;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,iBAAgB,iBAAiB;AACjC;AACA;AACA;AACA;AACA,aAAY,oBAAoB;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACjDA;AACA;AACA;AACA;AACA,qBAAoB;AACpB;AACA;AACA;AACA;AACA;AACA;AACA,GAAE;AACF;AACA;AACA,GAAE;AACF;AACA;AACA,GAAE;AACF;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,iBAAgB,mBAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAgB,sBAAsB;AACtC;AACA;AACA,mBAAkB,2BAA2B;AAC7C;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,gBAAe,mBAAmB;AAClC;AACA;AACA;AACA;AACA,kBAAiB,2BAA2B;AAC5C;AACA;AACA,SAAQ,uBAAuB;AAC/B;AACA;AACA,IAAG;AACH;AACA,kBAAiB,uBAAuB;AACxC;AACA;AACA,4BAA2B;AAC3B;AACA;AACA;;AAEA;AACA;AACA;AACA,gBAAe,iBAAiB;AAChC;AACA;AACA;AACA;AACA;AACA,eAAc;AACd;AACA,iCAAgC,sBAAsB;AACtD;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,IAAG;AACH;AACA,IAAG;AACH;AACA;AACA;AACA,GAAE;AACF;AACA,GAAE;AACF;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAE;AACF;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA,IAAG;AACH;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,EAAC;;AAED;AACA;;AAEA;AACA;AACA,GAAE;AACF;AACA;AACA;AACA;AACA;AACA,IAAG;AACH;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,GAAE;AACF;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,wDAAuD;AACvD;;AAEA,8BAA6B,mBAAmB;;AAEhD;;AAEA;;AAEA;AACA;AACA;;;;;;;;;;;;mBC5OwBuL,G;AATxB;AACA;;;;;;;;AAQe,UAASA,GAAT,CAAaC,EAAb,EAAiB3I,IAAjB,EAAuB;;AAErC,MAAM7C,OAAO,EAAb;;AAEA,WAASC,IAAT,GAAgB;AACfC;AACAoC;AACAmJ;AACAC;AACA;;AAED,WAASxL,YAAT,GAAwB;AACvBF,QAAK2L,gBAAL,GAAwB,KAAxB;AACA3L,QAAK6C,IAAL,GAAYA,IAAZ;AACA;;AAED,WAASP,OAAT,GAAmB;AAClBsJ;AACAC;AACA;;AAED,WAASD,cAAT,GAA0B;AACzB5L,QAAKyD,OAAL,GAAezD,KAAK6C,IAAL,CAAUY,OAAzB;AACAzD,QAAKkB,MAAL,GAAcsK,GAAGzL,MAAH,CAAUkB,GAAV,CAAcC,MAA5B;AACAlB,QAAKwI,CAAL,GAASsD,WAAT;AACA9L,QAAKyI,CAAL,GAASsD,WAAT;AACA/L,QAAKmB,aAAL,GAAqBqK,GAAGzL,MAAH,CAAUkB,GAAV,CAAcE,aAAnC;AACAnB,QAAKgM,QAAL,GAAgB,CAAChM,KAAK6C,IAAL,CAAUK,IAAV,CAAeE,IAAf,CAAoBpD,KAAK6C,IAAL,CAAUE,MAA9B,EAAsC,OAAtC,IAAiD,EAAlD,IAAwDyI,GAAGzL,MAAH,CAAUgB,IAAlF;AACAf,QAAKiG,KAAL,GAAauF,GAAGzL,MAAH,CAAUe,YAAV,GAAyBd,KAAKgM,QAA3C;AACAhM,QAAKiM,cAAL,GAAsBT,GAAGzL,MAAH,CAAUe,YAAV,GAAyBd,KAAKgM,QAA9B,IAA0ChM,KAAK6C,IAAL,CAAUqJ,QAAV,GAAqB,GAA/D,KAAuE,CAA7F;AACAlM,QAAK2F,KAAL,GAAa6F,GAAG/G,MAAH,CAAUkB,KAAV,GAAkBhB,QAAlB,CAA2B,aAA3B,EAA0CA,QAA1C,CAAmD3E,KAAK6C,IAAL,CAAUsJ,YAAV,IAA0B,EAA7E,CAAb;AACAnM,QAAKoM,SAAL,GAAiBZ,GAAG/G,MAAH,CAAUkB,KAAV,GAAkBhB,QAAlB,CAA2B,WAA3B,EAAwC2C,QAAxC,CAAiDtH,KAAK2F,KAAtD,CAAjB;AACA3F,QAAKqM,YAAL,GAAoBb,GAAG/G,MAAH,CAAUkB,KAAV,GAAkBhB,QAAlB,CAA2B,cAA3B,EAA2C2C,QAA3C,CAAoDtH,KAAK2F,KAAzD,CAApB;AACA;;AAED,WAASkG,eAAT,GAA2B;AAC1BnH,QAAK4H,MAAL,CAAY,UAAU5H,IAAV,EAAgB6H,OAAhB,EAAyBC,KAAzB,EAAgCC,MAAhC,EAAwCC,QAAxC,EAAkD;AAC7DH,YAAQI,SAAR,CAAkBC,IAAlB,GAAyB,YAAY;AACpC,YAAO,CAAC,KAAKhH,IAAL,CAAU,GAAV,CAAR;AACA,KAFD;AAGA2G,YAAQI,SAAR,CAAkBE,IAAlB,GAAyB,YAAY;AACpC,YAAO,CAAC,KAAKjH,IAAL,CAAU,GAAV,CAAR;AACA,KAFD;AAGA2G,YAAQI,SAAR,CAAkBG,QAAlB,GAA6B,YAAY;AACxC,YAAO,CAAC,KAAKlH,IAAL,CAAU,OAAV,CAAR;AACA,KAFD;AAGA2G,YAAQI,SAAR,CAAkBI,SAAlB,GAA8B,YAAY;AACzC,YAAO,CAAC,KAAKnH,IAAL,CAAU,QAAV,CAAR;AACA,KAFD;AAGA2G,YAAQI,SAAR,CAAkBK,OAAlB,GAA4B,YAAY;AACvC,YAAO,KAAKJ,IAAL,KAAc,KAAKE,QAAL,EAArB;AACA,KAFD;AAGA,IAhBD;AAiBA;;AAED,WAASrB,IAAT,GAAgB;AACfwB;AACAC;AACAC;AACAC;AACA;;AAED,WAASH,QAAT,GAAoB;AACnBjN,QAAKqN,IAAL,GAAY7B,GAAG/G,MAAH,CAAU4C,IAAV,CAAerH,KAAKwI,CAApB,EAAuBxI,KAAKyI,CAA5B,EACXzI,KAAKiG,KADM,EACCjG,KAAKkB,MADN,EAEXlB,KAAKmB,aAFM,EAESnB,KAAKmB,aAFd,EAGVwD,QAHU,CAGD,KAHC,EAIV2C,QAJU,CAIDtH,KAAKoM,SAJJ,CAAZ;AAKA,OAAIpM,KAAKyD,OAAT,EAAkB;AACjBzD,SAAKqN,IAAL,CAAU1I,QAAV,CAAmB,aAAnB;AACA;AACD;;AAED,WAASuI,iBAAT,GAA6B;AAC5B,OAAIlN,KAAKyD,OAAT,EAAkB;AAClBzD,QAAKsN,aAAL,GAAqB9B,GAAG/G,MAAH,CAAU4C,IAAV,CAAerH,KAAKwI,CAApB,EAAuBxI,KAAKyI,CAA5B,EACpBzI,KAAKiM,cADe,EACCjM,KAAKkB,MADN,EAEpBlB,KAAKmB,aAFe,EAEAnB,KAAKmB,aAFL,EAGnBwD,QAHmB,CAGV,cAHU,EAInB2C,QAJmB,CAIVtH,KAAKoM,SAJK,CAArB;AAKA;;AAED,WAASe,UAAT,GAAsB;AACrB3B,MAAG/G,MAAH,CAAUmE,IAAV,CAAe5I,KAAKwI,CAAL,GAASxI,KAAKiG,KAAL,GAAa,CAArC,EACCjG,KAAKyI,CAAL,GAASzI,KAAKkB,MAAL,GAAc,CADxB,EAEClB,KAAK6C,IAAL,CAAUkI,IAFX,EAGEpG,QAHF,CAGW,WAHX,EAIE2C,QAJF,CAIWtH,KAAKoM,SAJhB;AAKAmB;AACA;;AAED,WAASH,mBAAT,GAA+B;AAC9B,OAAIpN,KAAKyD,OAAT,EAAkB;;AAElB,OAAMxC,MAAMjB,KAAKqN,IAAjB;AAAA,OACCG,eAAe,CADhB;;AAGAhC,MAAG/G,MAAH,CAAU4C,IAAV,CAAepG,IAAI2L,IAAJ,KAAa3L,IAAI6L,QAAJ,EAAb,GAA8B,CAA7C,EAAgD7L,IAAI4L,IAAJ,KAAa,CAA7D,EACCW,YADD,EACexN,KAAKkB,MAAL,GAAc,CAD7B,EACgClB,KAAKmB,aADrC,EACoDnB,KAAKmB,aADzD,EAEEwD,QAFF,CAEW,cAFX,EAGE2C,QAHF,CAGWtH,KAAKqM,YAHhB;AAIAb,MAAG/G,MAAH,CAAU4C,IAAV,CAAepG,IAAI2L,IAAJ,KAAa,CAA5B,EAA+B3L,IAAI4L,IAAJ,KAAa,CAA5C,EACCW,YADD,EACexN,KAAKkB,MAAL,GAAc,CAD7B,EACgClB,KAAKmB,aADrC,EACoDnB,KAAKmB,aADzD,EAEEwD,QAFF,CAEW,aAFX,EAGE2C,QAHF,CAGWtH,KAAKqM,YAHhB;;AAKA,OAAIrM,KAAK6C,IAAL,CAAUqJ,QAAV,IAAsBlM,KAAK6C,IAAL,CAAUqJ,QAAV,GAAqB,GAA/C,EAAoD;AACnDV,OAAG/G,MAAH,CAAUgJ,OAAV,CAAkBC,6BAAlB,EACE/I,QADF,CACW,iBADX,EAEE2C,QAFF,CAEWtH,KAAKqM,YAFhB;AAGA;AACD;;AAED,WAASqB,2BAAT,GAAuC;AACtC,OAAMC,eAAe3N,KAAKsN,aAA1B;AACA,UAAO,CACNK,aAAaX,OAAb,KAAyB,CADnB,EACsBW,aAAad,IAAb,KAAsBc,aAAaZ,SAAb,EAD5C,EAENY,aAAaX,OAAb,KAAyB,CAFnB,EAEsBW,aAAad,IAAb,KAAsBc,aAAaZ,SAAb,EAF5C,EAGNY,aAAaX,OAAb,EAHM,EAGkBW,aAAad,IAAb,KAAsBc,aAAaZ,SAAb,EAAtB,GAAiD,IAHnE,CAAP;AAKA;;AAED,WAASrB,IAAT,GAAgB;AACf,OAAI1L,KAAKyD,OAAT,EAAkB;AAClBmK;AACAC;AACAC;AACAC;AACAC;AACA;;AAED,WAASH,YAAT,GAAwB;AACvB,OAAMI,gBAAgBzC,GAAGtJ,cAAH,CAAkBoI,OAAxC;AACAtK,QAAKkO,WAAL,GAAmBD,cACjB9H,MADiB,mCACqBnG,KAAK6C,IAAL,CAAUmB,EAD/B,SAAnB;;AAGA,OAAI,CAAChE,KAAKkO,WAAV,EAAuB;AACtBlO,SAAKkO,WAAL,GAAmB1C,GAAG/G,MAAH,CAAUkB,KAAV,GACjBhB,QADiB,CACR,sBADQ,EAEjBiB,IAFiB,CAEZ,WAFY,EAEC5F,KAAK6C,IAAL,CAAUmB,EAFX,EAGjBsD,QAHiB,CAGR2G,aAHQ,CAAnB;;AAKAE;;AAEA,QAAMC,IAAI5C,GAAG/G,MAAH,CAAUV,MAAV,CACTW,KAAKX,MAAL,CAAYsK,MAAZ,CAAmB,CAAnB,EAAsB,CAAtB,EAAyB,CAAzB,EAA4B,MAA5B,EAAoC,GAApC,CADS,CAAV;AAEArO,SAAKkO,WAAL,CAAiBtI,IAAjB,CAAsB;AACrB7B,aAAQqK;AADa,KAAtB;AAGA;;AAEDpO,QAAK2F,KAAL,CAAW0E,KAAX,CAAiB,UAACiE,CAAD,EAAO;AACvB,QAAItO,KAAK2L,gBAAT,EAA2B;AAC1B;AACA;AACA;AACDsC,kBAAc1D,SAAd,CAAwB,kBAAxB,EACEC,OADF,CACU;AAAA,YAAMC,GAAG9F,QAAH,CAAY,MAAZ,CAAN;AAAA,KADV;AAEA3E,SAAKkO,WAAL,CAAiBxD,WAAjB,CAA6B,MAA7B;AACA,IARD;AASA;;AAED,WAASyD,cAAT,GAA0B;AAAA,+BACVI,sBADU;AAAA,OAClB/F,CADkB,yBAClBA,CADkB;AAAA,OACfC,CADe,yBACfA,CADe;;AAEzBzI,QAAKkO,WAAL,CAAiBM,SAAjB,OAA+BhG,CAA/B,SAAoCC,CAApC;AACAzI,QAAKkO,WAAL,CAAiBtJ,KAAjB;;AAEA,OAAM6J,OAAOC,kBAAb;AACA,OAAMC,iBACLjK,KAAKkK,KAAL,iHAEIH,IAFJ,iDADD;AAMAzO,QAAKkO,WAAL,CAAiBW,MAAjB,CAAwBF,cAAxB;AACA;;AAED,WAASD,gBAAT,GAA4B;;AAE3B;AACA,OAAGlD,GAAGzL,MAAH,CAAUyB,iBAAb,EAAgC;AAC/B,QAAMiN,QAAOjD,GAAGzL,MAAH,CAAUyB,iBAAvB;AACA,QAAG,OAAOiN,KAAP,KAAgB,QAAnB,EAA6B;AAC5B,YAAOA,KAAP;AACA;AACD,QAAGK,WAAWL,KAAX,CAAH,EAAqB;AACpB,YAAOA,MAAK5L,IAAL,CAAP;AACA;AACD;;AAED,OAAMkM,aAAa/O,KAAK6C,IAAL,CAAUE,MAAV,CAAiBwF,MAAjB,CAAwB,OAAxB,CAAnB;AACA,OAAMyG,WAAWhP,KAAK6C,IAAL,CAAUK,IAAV,CAAeqF,MAAf,CAAsB,OAAtB,CAAjB;AACA,OAAM0G,UAAajP,KAAK6C,IAAL,CAAUkI,IAAvB,UAAgCgE,UAAhC,WAAgDC,QAAtD;;AAEA,OAAME,wBAAsBlP,KAAKgM,QAA3B,UAAN;AACA,OAAMmD,SAASnP,KAAK6C,IAAL,CAAUqJ,QAAV,kBAAkClM,KAAK6C,IAAL,CAAUqJ,QAA5C,GAAyD,IAAxE;;AAEA,OAAMuC,iEAEEQ,OAFF,0BAGCC,MAHD,uBAKHC,iBAAeA,MAAf,YAA8B,EAL3B,0BAAN;AASA,UAAOV,IAAP;AACA;;AAED,WAASF,oBAAT,GAAgC;AAC/B,UAAO;AACN/F,OAAGxI,KAAKqN,IAAL,CAAUL,OAAV,KAAsB,CADnB;AAENvE,OAAGzI,KAAKqN,IAAL,CAAUR,IAAV,KAAmB;AAFhB,IAAP;AAIA;;AAED,WAASiB,WAAT,GAAuB;AAAA,sBACEsB,aADF;AAAA,OACdC,IADc,gBACdA,IADc;AAAA,OACRC,KADQ,gBACRA,KADQ;;AAGtBD,QAAKE,IAAL,CAAUC,WAAV,EAAuBC,OAAvB,EAAgCC,WAAhC;AACAJ,SAAMC,IAAN,CAAWI,YAAX,EAAyBF,OAAzB,EAAkCG,YAAlC;;AAEA,YAASD,YAAT,CAAsBE,EAAtB,EAA0BC,EAA1B,EAA8B;AAC7BC,wBAAoBF,EAApB,EAAwBC,EAAxB;AACA;AACD,YAASF,YAAT,GAAwB;AACvBI;AACA;;AAED,YAASR,WAAT,CAAqBK,EAArB,EAAyBC,EAAzB,EAA6B;AAC5BG,uBAAmBJ,EAAnB,EAAuBC,EAAvB;AACA;AACD,YAASJ,WAAT,GAAuB;AACtBQ;AACA;AACD;;AAED,WAASd,WAAT,GAAuB;AACtB,UAAO;AACNC,UAAMrP,KAAKqM,YAAL,CAAkBlG,MAAlB,CAAyB,cAAzB,CADA;AAENmJ,WAAOtP,KAAKqM,YAAL,CAAkBlG,MAAlB,CAAyB,eAAzB;AAFD,IAAP;AAIA;;AAED,WAAS4H,SAAT,GAAqB;AACpB/N,QAAKoM,SAAL,CAAemD,IAAf,CAAoBY,MAApB,EAA4BV,OAA5B,EAAqCW,MAArC;AACA;;AAED,WAASpC,oBAAT,GAAgC;AAC/B,OAAM/M,MAAMjB,KAAKqN,IAAjB;AAAA,OACCM,eAAe3N,KAAKsN,aADrB;AAAA,OAEC+C,SAASrQ,KAAK2F,KAAL,CAAWQ,MAAX,CAAkB,kBAAlB,CAFV;AAGAkK,aAAUA,OAAOd,IAAP,CAAYe,OAAZ,EAAqBC,QAArB,EAA+BC,OAA/B,CAAV;;AAEA,YAASF,OAAT,CAAiBT,EAAjB,EAAqBC,EAArB,EAAyB;AACxB,QAAID,KAAKlC,aAAa8C,MAAtB,EAA8B;AAC7BZ,UAAKlC,aAAa8C,MAAlB;AACA;AACD,QAAIZ,KAAKlC,aAAa+C,MAAtB,EAA8B;AAC7Bb,UAAKlC,aAAa+C,MAAlB;AACA;;AAED/C,iBAAa/H,IAAb,CAAkB,OAAlB,EAA2B+H,aAAagD,MAAb,GAAsBd,EAAjD;AACAQ,WAAOzK,IAAP,CAAY,QAAZ,EAAsB8H,6BAAtB;AACAC,iBAAaiD,OAAb,GAAuBf,EAAvB;AACA;AACD,YAASW,OAAT,GAAmB;AAClB,QAAI,CAAC7C,aAAaiD,OAAlB,EAA2B;AAC3BC;AACAC;AACA;AACD,YAASP,QAAT,GAAoB;AACnB5C,iBAAaiD,OAAb,GAAuB,CAAvB;AACAjD,iBAAagD,MAAb,GAAsBhD,aAAab,QAAb,EAAtB;AACAa,iBAAa+C,MAAb,GAAsB,CAAC/C,aAAab,QAAb,EAAvB;AACAa,iBAAa8C,MAAb,GAAsBxP,IAAI6L,QAAJ,KAAiBa,aAAab,QAAb,EAAvC;AACA;AACD;;AAED,WAAS2C,OAAT,GAAmB;AAClB,OAAMxO,MAAMjB,KAAKqN,IAAjB;AACApM,OAAI8P,EAAJ,GAAS9P,IAAI2L,IAAJ,EAAT;AACA3L,OAAI+P,EAAJ,GAAS/P,IAAI4L,IAAJ,EAAT;AACA5L,OAAI0P,MAAJ,GAAa1P,IAAI6L,QAAJ,EAAb;AACA7L,OAAI2P,OAAJ,GAAc,CAAd;AACAK,+BAA4B,SAA5B;AACA;AACDjR,OAAKyP,OAAL,GAAeA,OAAf;;AAEA,WAASU,MAAT,CAAgBN,EAAhB,EAAoBC,EAApB,EAAwB;AACvB,OAAM7O,MAAMjB,KAAKqN,IAAjB;AACApM,OAAI2P,OAAJ,GAAcM,kBAAkBrB,EAAlB,CAAd;AACAsB,uBAAoB,EAAC3I,GAAGvH,IAAI8P,EAAJ,GAAS9P,IAAI2P,OAAjB,EAApB;AACAK,+BAA4B,QAA5B,EAAsC,CAACpB,EAAD,EAAKC,EAAL,CAAtC;AACA;AACD9P,OAAKmQ,MAAL,GAAcA,MAAd;;AAEA,WAASC,MAAT,GAAkB;AACjB,OAAMnP,MAAMjB,KAAKqN,IAAjB;AACA,OAAI,CAACpM,IAAI2P,OAAT,EAAkB;AAClBQ;AACAN;AACAG,+BAA4B,QAA5B;AACA;AACDjR,OAAKoQ,MAAL,GAAcA,MAAd;;AAEA,WAASH,kBAAT,CAA4BJ,EAA5B,EAAgCC,EAAhC,EAAoC;AACnC,OAAM7O,MAAMjB,KAAKqN,IAAjB;AACApM,OAAI2P,OAAJ,GAAcM,kBAAkBrB,EAAlB,CAAd;AACAsB,uBAAoB;AACnB3I,OAAGvH,IAAI8P,EAAJ,GAAS9P,IAAI2P,OADG;AAEnB3K,WAAOhF,IAAI0P,MAAJ,GAAa1P,IAAI2P;AAFL,IAApB;AAIAK,+BAA4B,QAA5B,EAAsC,CAACpB,EAAD,EAAKC,EAAL,CAAtC;AACA;AACD9P,OAAKiQ,kBAAL,GAA0BA,kBAA1B;;AAEA,WAASC,kBAAT,GAA8B;AAC7B,OAAMjP,MAAMjB,KAAKqN,IAAjB;AACA,OAAIpM,IAAI2P,OAAR,EAAiBQ;AACjBN;AACAG,+BAA4B,QAA5B;AACA;AACDjR,OAAKkQ,kBAAL,GAA0BA,kBAA1B;;AAEA,WAASe,2BAAT,CAAqCI,EAArC,EAAyChG,IAAzC,EAA+C;AAC9C,OAAMiG,KAAK9F,GAAGtH,cAAd;AACA,OAAIoN,GAAGtR,KAAK6C,IAAL,CAAUmB,EAAb,CAAJ,EAAsB;AAAA;AAAA;AAAA;;AAAA;AACrB,0BAAoBsN,GAAGtR,KAAK6C,IAAL,CAAUmB,EAAb,CAApB,8HAAsC;AAAA,UAA7BuN,OAA6B;;AACrC,UAAMC,KAAKhG,GAAGlL,OAAH,CAAWiR,OAAX,CAAX;AACAC,SAAGH,EAAH,EAAO/F,KAAP,CAAakG,EAAb,EAAiBnG,IAAjB;AACA;AAJoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKrB;AACD;;AAED,WAAS0E,mBAAT,CAA6BF,EAA7B,EAAiCC,EAAjC,EAAqC;AACpC,OAAM7O,MAAMjB,KAAKqN,IAAjB;AACApM,OAAI2P,OAAJ,GAAcM,kBAAkBrB,EAAlB,CAAd;AACAsB,uBAAoB,EAAClL,OAAOhF,IAAI0P,MAAJ,GAAa1P,IAAI2P,OAAzB,EAApB;AACA;;AAED,WAASZ,mBAAT,GAA+B;AAC9B,OAAM/O,MAAMjB,KAAKqN,IAAjB;AACA,OAAIpM,IAAI2P,OAAR,EAAiBQ;AACjBN;AACA;;AAED,WAASK,mBAAT,OAAuD;AAAA,qBAAzB3I,CAAyB;AAAA,OAAzBA,CAAyB,0BAArB,IAAqB;AAAA,yBAAfvC,KAAe;AAAA,OAAfA,KAAe,8BAAP,IAAO;;AACtD,OAAMhF,MAAMjB,KAAKqN,IAAjB;AACA,OAAI7E,CAAJ,EAAO;AACN;AACA,QAAMiJ,KAAK5O,KAAKa,YAAL,CAAkBd,GAAlB,CAAsB,eAAO;AACvC,YAAO4I,GAAGlL,OAAH,CAAW0J,GAAX,EAAgBqD,IAAhB,CAAqBT,IAArB,EAAP;AACA,KAFU,CAAX;AAGA;AACA,QAAM8E,UAAUD,GAAGhL,MAAH,CAAU,UAACkL,IAAD,EAAOhL,IAAP,EAAgB;AACzC,YAAO6B,KAAK7B,IAAZ;AACA,KAFe,EAEb6B,CAFa,CAAhB;AAGA,QAAG,CAACkJ,OAAJ,EAAa;AACZzL,aAAQ,IAAR;AACA;AACA;AACD2L,gBAAY3Q,GAAZ,EAAiB,GAAjB,EAAsBuH,CAAtB;AACA;AACD,OAAIvC,SAASA,SAASuF,GAAGzL,MAAH,CAAUe,YAAhC,EAA8C;AAC7C8Q,gBAAY3Q,GAAZ,EAAiB,OAAjB,EAA0BgF,KAA1B;AACA;AACDsH;AACAsE;AACAC;AACAC;AACAC;AACA;;AAED,WAASpE,iBAAT,GAA6B;AAC5B5N,QAAK2F,KAAL,CAAW0E,KAAX,CAAiB,YAAY;AAC5B,QAAIrK,KAAK2L,gBAAT,EAA2B;AAC1B;AACA;AACA;AACD,QAAI3L,KAAK2F,KAAL,CAAWsM,QAAX,CAAoB,QAApB,CAAJ,EAAmC;AAClCzG,QAAGjL,aAAH,CAAiB,OAAjB,EAA0B,CAACP,KAAK6C,IAAN,CAA1B;AACA;AACD2I,OAAGpL,YAAH;AACAJ,SAAK2F,KAAL,CAAWuM,WAAX,CAAuB,QAAvB;AACA,IAVD;AAWA;;AAED,WAASd,YAAT,GAAwB;AAAA,+BACkBe,wBADlB;AAAA,OACfC,cADe,yBACfA,cADe;AAAA,OACCC,YADD,yBACCA,YADD;;AAEvBrS,QAAK6C,IAAL,CAAUE,MAAV,GAAmBqP,cAAnB;AACApS,QAAK6C,IAAL,CAAUK,IAAV,GAAiBmP,YAAjB;AACAlE;AACA3C,MAAGjL,aAAH,CAAiB,aAAjB,EACC,CAACP,KAAK6C,IAAN,EAAYuP,cAAZ,EAA4BC,YAA5B,CADD;AAEA;;AAED,WAASxB,gBAAT,GAA4B;AAC3B,OAAMyB,eAAeC,kBAArB;AACAvS,QAAK6C,IAAL,CAAUqJ,QAAV,GAAqBoG,YAArB;AACAnE;AACA3C,MAAGjL,aAAH,CAAiB,iBAAjB,EACC,CAACP,KAAK6C,IAAN,EAAYyP,YAAZ,CADD;AAEA;;AAED,WAASxB,oBAAT,GAAgC;AAC/B9Q,QAAK2L,gBAAL,GAAwB,IAAxB;AACA6G,cAAW;AAAA,WAAMxS,KAAK2L,gBAAL,GAAwB,KAA9B;AAAA,IAAX,EAAgD,IAAhD;AACA;;AAED,WAASwG,sBAAT,GAAkC;AACjC,OAAMlR,MAAMjB,KAAKqN,IAAjB;AACA,OAAMoF,aAAaxR,IAAI2L,IAAJ,KAAapB,GAAGzL,MAAH,CAAUe,YAA1C;AACA,OAAMsR,iBAAiB5G,GAAGnH,WAAH,CAAeb,KAAf,GAAuBD,GAAvB,CAA2BkP,aAAajH,GAAGzL,MAAH,CAAUgB,IAAlD,EAAwD,OAAxD,CAAvB;AACA,OAAM2R,iBAAiBzR,IAAI6L,QAAJ,KAAiBtB,GAAGzL,MAAH,CAAUe,YAAlD;AACA,OAAMuR,eAAeD,eAAe5O,KAAf,GAAuBD,GAAvB,CAA2BmP,iBAAiBlH,GAAGzL,MAAH,CAAUgB,IAAtD,EAA4D,OAA5D,CAArB;AACA;AACA;AACA;AACA;AACA;AACAsR,gBAAa9O,GAAb,CAAiB,IAAjB,EAAuB,SAAvB;AACA,UAAO,EAAE6O,8BAAF,EAAkBC,0BAAlB,EAAP;AACA;;AAED,WAASE,gBAAT,GAA4B;AAC3B,OAAMrG,WAAWlM,KAAKsN,aAAL,CAAmBR,QAAnB,KAAgC9M,KAAKqN,IAAL,CAAUP,QAAV,EAAhC,GAAuD,GAAxE;AACA,UAAO6F,SAASzG,QAAT,EAAmB,EAAnB,CAAP;AACA;;AAED,WAASJ,SAAT,GAAqB;AACpB,OAAItD,IAAIxI,KAAK6C,IAAL,CAAUE,MAAV,CAAiBK,IAAjB,CAAsBoI,GAAGnH,WAAzB,EAAsC,OAAtC,IACPmH,GAAGzL,MAAH,CAAUgB,IADH,GACUyK,GAAGzL,MAAH,CAAUe,YAD5B;;AAGA,OAAI0K,GAAGnL,OAAH,CAAW,OAAX,CAAJ,EAAyB;AACxBmI,QAAIxI,KAAK6C,IAAL,CAAUE,MAAV,CAAiBK,IAAjB,CAAsBoI,GAAGnH,WAAzB,EAAsC,MAAtC,IACHmH,GAAGzL,MAAH,CAAUe,YADP,GACsB,EAD1B;AAEA;AACD,UAAO0H,CAAP;AACA;;AAED,WAASuD,SAAT,GAAqB;AACpB,UAAOP,GAAGzL,MAAH,CAAUc,aAAV,GAA0B2K,GAAGzL,MAAH,CAAUuB,OAApC,GACNtB,KAAK6C,IAAL,CAAUQ,MAAV,IAAoBrD,KAAKkB,MAAL,GAAcsK,GAAGzL,MAAH,CAAUuB,OAA5C,CADD;AAEA;;AAED,WAAS4P,iBAAT,CAA2BrB,EAA3B,EAA+B;AAC9B,OAAI+C,MAAM/C,EAAV;AAAA,OAAcgD,YAAd;AAAA,OAAmBC,iBAAnB;;AAEA,OAAItH,GAAGnL,OAAH,CAAW,MAAX,CAAJ,EAAwB;AACvBwS,UAAMhD,MAAMrE,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,CAA/B,CAAN;AACAgS,eAAWF,MAAMC,GAAN,IACRA,MAAMrH,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,EAAhC,GAAsC,CAAtC,GAA0C0K,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,CAD1D,CAAX;AAEA,IAJD,MAIO,IAAI0K,GAAGnL,OAAH,CAAW,OAAX,CAAJ,EAAyB;AAC/BwS,UAAMhD,MAAMrE,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,EAA/B,CAAN;AACAgS,eAAWF,MAAMC,GAAN,IACRA,MAAMrH,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,EAAhC,GAAsC,CAAtC,GAA0C0K,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,EAD1D,CAAX;AAEA,IAJM,MAIA;AACN+R,UAAMhD,KAAKrE,GAAGzL,MAAH,CAAUe,YAArB;AACAgS,eAAWF,MAAMC,GAAN,IACRA,MAAMrH,GAAGzL,MAAH,CAAUe,YAAV,GAAyB,CAAhC,GAAqC,CAArC,GAAyC0K,GAAGzL,MAAH,CAAUe,YAD1C,CAAX;AAEA;AACD,UAAOgS,QAAP;AACA;;AAED,WAASlB,WAAT,CAAqB/R,OAArB,EAA8B+F,IAA9B,EAAoCmN,KAApC,EAA2C;AAC1CA,WAAQ,CAACA,KAAT;AACA,OAAI,CAACC,MAAMD,KAAN,CAAL,EAAmB;AAClBlT,YAAQ+F,IAAR,CAAaA,IAAb,EAAmBmN,KAAnB;AACA;AACD,UAAOlT,OAAP;AACA;;AAED,WAASiS,2BAAT,GAAuC;AACtC9R,QAAKsN,aAAL,CAAmB1H,IAAnB,CAAwB,GAAxB,EAA6B5F,KAAKqN,IAAL,CAAUT,IAAV,EAA7B;AACA5M,QAAKsN,aAAL,CAAmB1H,IAAnB,CAAwB,OAAxB,EAAiC5F,KAAKqN,IAAL,CAAUP,QAAV,MAAwB9M,KAAK6C,IAAL,CAAUqJ,QAAV,GAAqB,GAA7C,CAAjC;AACA;;AAED,WAASqB,qBAAT,GAAiC;AAChC,OAAMtM,MAAMjB,KAAKqN,IAAjB;AAAA,OACC4F,QAAQjT,KAAK2F,KAAL,CAAWQ,MAAX,CAAkB,YAAlB,CADT;AAEA,OAAI8M,MAAM7J,OAAN,GAAgBnD,KAAhB,GAAwBhF,IAAI6L,QAAJ,EAA5B,EAA4C;AAC3CmG,UAAMtO,QAAN,CAAe,KAAf,EAAsBiB,IAAtB,CAA2B,GAA3B,EAAgC3E,IAAI2L,IAAJ,KAAa3L,IAAI6L,QAAJ,EAAb,GAA8B,CAA9D;AACA,IAFD,MAEO;AACNmG,UAAMvI,WAAN,CAAkB,KAAlB,EAAyB9E,IAAzB,CAA8B,GAA9B,EAAmC3E,IAAI2L,IAAJ,KAAa3L,IAAI6L,QAAJ,KAAiB,CAAjE;AACA;AACD;;AAED,WAAS+E,sBAAT,GAAkC;AACjC,OAAM5Q,MAAMjB,KAAKqN,IAAjB;AACArN,QAAKqM,YAAL,CAAkBlG,MAAlB,CAAyB,cAAzB,EAAyCP,IAAzC,CAA8C;AAC7C,SAAK3E,IAAI2L,IAAJ,KAAa;AAD2B,IAA9C;AAGA5M,QAAKqM,YAAL,CAAkBlG,MAAlB,CAAyB,eAAzB,EAA0CP,IAA1C,CAA+C;AAC9C,SAAK3E,IAAI+L,OAAJ,KAAgB;AADyB,IAA/C;AAGA,OAAMqD,SAASrQ,KAAK2F,KAAL,CAAWQ,MAAX,CAAkB,kBAAlB,CAAf;AACAkK,aAAUA,OAAOzK,IAAP,CAAY,QAAZ,EAAsB8H,6BAAtB,CAAV;AACA;;AAED,WAASqE,qBAAT,GAAiC;AAAA;AAAA;AAAA;;AAAA;AAChC,0BAAkB/R,KAAK6J,MAAvB,mIAA+B;AAAA,SAAtBzI,KAAsB;;AAC9BA,WAAM8R,MAAN;AACA;AAH+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIhC;;AAED,WAASlB,uBAAT,GAAmC;AAAA,gCACnBzD,sBADmB;AAAA,OAC3B/F,CAD2B,0BAC3BA,CAD2B;AAAA,OACxBC,CADwB,0BACxBA,CADwB;;AAElCzI,QAAKkO,WAAL,IAAoBlO,KAAKkO,WAAL,CAAiBM,SAAjB,OAA+BhG,CAA/B,SAAoCC,CAApC,CAApB;AACA;;AAED,WAASqG,UAAT,CAAoBqE,eAApB,EAAqC;AACpC,OAAIC,UAAU,EAAd;AACA,UAAOD,mBAAmBC,QAAQlI,QAAR,CAAiBmI,IAAjB,CAAsBF,eAAtB,MAA2C,mBAArE;AACA;;AAEDlT;;AAEA,SAAOD,IAAP;AACA;;;;;;;;;;;;mBCrgBuBsT,K;AAXxB;AACA;;;;;;;;;;AAUe,UAASA,KAAT,CAAe9H,EAAf,EAAmBrB,SAAnB,EAA8BC,OAA9B,EAAuC;;AAErD,MAAMpK,OAAO,EAAb;;AAEA,WAASC,IAAT,GAAgB;AACfD,QAAKmK,SAAL,GAAiBA,SAAjB;AACAnK,QAAKoK,OAAL,GAAeA,OAAf;AACA9H;AACAmJ;AACA;;AAED,WAASnJ,OAAT,GAAmB;;AAElBtC,QAAKuT,OAAL,GAAepJ,UAAUkD,IAAV,CAAeT,IAAf,KAAwBzC,UAAUkD,IAAV,CAAeP,QAAf,KAA4B,CAAnE;;AAEA,OAAM0G,YAAY,SAAZA,SAAY;AAAA,WACjBpJ,QAAQiD,IAAR,CAAaT,IAAb,KAAsB5M,KAAKuT,OAAL,GAAe/H,GAAGzL,MAAH,CAAUuB,OAA/C,IACCtB,KAAKuT,OAAL,GAAepJ,UAAUkD,IAAV,CAAeT,IAAf,KAAwBpB,GAAGzL,MAAH,CAAUuB,OAFjC;AAAA,IAAlB;;AAIA,UAAMkS,WAAN,EAAmB;AAClBxT,SAAKuT,OAAL,IAAgB,EAAhB;AACA;;AAEDvT,QAAKyT,OAAL,GAAejI,GAAGzL,MAAH,CAAUc,aAAV,GAA0B2K,GAAGzL,MAAH,CAAUkB,GAAV,CAAcC,MAAxC,GACd,CAACsK,GAAGzL,MAAH,CAAUuB,OAAV,GAAoBkK,GAAGzL,MAAH,CAAUkB,GAAV,CAAcC,MAAnC,IAA6CiJ,UAAUtH,IAAV,CAAeQ,MAD9C,GAEdmI,GAAGzL,MAAH,CAAUuB,OAFX;;AAIAtB,QAAK0T,KAAL,GAAatJ,QAAQiD,IAAR,CAAaT,IAAb,KAAsBpB,GAAGzL,MAAH,CAAUuB,OAAV,GAAoB,CAAvD;AACAtB,QAAK2T,KAAL,GAAanI,GAAGzL,MAAH,CAAUc,aAAV,GAA0B2K,GAAGzL,MAAH,CAAUkB,GAAV,CAAcC,MAAd,GAAuB,CAAjD,GACZ,CAACsK,GAAGzL,MAAH,CAAUuB,OAAV,GAAoBkK,GAAGzL,MAAH,CAAUkB,GAAV,CAAcC,MAAnC,IAA6CkJ,QAAQvH,IAAR,CAAaQ,MAD9C,GAEZmI,GAAGzL,MAAH,CAAUuB,OAFX;;AAIA,OAAMsS,mBAAoBzJ,UAAUtH,IAAV,CAAeQ,MAAf,GAAwB+G,QAAQvH,IAAR,CAAaQ,MAA/D;AACArD,QAAKqB,KAAL,GAAamK,GAAGzL,MAAH,CAAUqB,KAAV,CAAgBC,KAA7B;AACArB,QAAK6T,SAAL,GAAiBD,mBAAmB,CAAnB,GAAuB,CAAxC;AACA5T,QAAK8T,OAAL,GAAeF,mBAAmB,CAAC5T,KAAKqB,KAAzB,GAAiCrB,KAAKqB,KAArD;AACArB,QAAK+T,MAAL,GAAcH,mBACb5T,KAAK2T,KAAL,GAAanI,GAAGzL,MAAH,CAAUqB,KAAV,CAAgBC,KADhB,GAEbrB,KAAK2T,KAAL,GAAanI,GAAGzL,MAAH,CAAUqB,KAAV,CAAgBC,KAF9B;;AAIArB,QAAKsI,IAAL,GACC5D,KAAK6D,MAAL,CAAY,sCACX,sDADW,GAEX,wCAFD,EAGC;AACCgL,aAASvT,KAAKuT,OADf;AAECE,aAASzT,KAAKyT,OAFf;AAGCC,WAAO1T,KAAK0T,KAHb;AAICC,WAAO3T,KAAK2T,KAJb;AAKCI,YAAQ/T,KAAK+T,MALd;AAMC1S,WAAOrB,KAAKqB,KANb;AAOCwS,eAAW7T,KAAK6T,SAPjB;AAQCC,aAAS9T,KAAK8T;AARf,IAHD,CADD;;AAeA,OAAG1J,QAAQiD,IAAR,CAAaT,IAAb,KAAsBzC,UAAUkD,IAAV,CAAeT,IAAf,KAAwBpB,GAAGzL,MAAH,CAAUuB,OAA3D,EAAoE;AACnEtB,SAAKsI,IAAL,GACC5D,KAAK6D,MAAL,CAAY,sCACZ,oDADY,GAEZ,kEAFY,GAGZ,sDAHY,GAIZ,wCAJA,EAKC;AACCgL,cAASvT,KAAKuT,OADf;AAECE,cAASzT,KAAKyT,OAFf;AAGCC,YAAO1T,KAAK0T,KAHb;AAICC,YAAO3T,KAAK2T,KAJb;AAKCK,aAAQxI,GAAGzL,MAAH,CAAUuB,OAAV,GAAoB,CAApB,GAAwBtB,KAAKqB,KALtC;AAMC4S,aAAQ7J,QAAQiD,IAAR,CAAaR,IAAb,KAAsBzC,QAAQiD,IAAR,CAAaN,SAAb,KAA2B,CAAjD,GAAqD/M,KAAK8T,OANnE;AAOCzE,WAAMjF,QAAQiD,IAAR,CAAaT,IAAb,KAAsBpB,GAAGzL,MAAH,CAAUuB,OAPvC;AAQCyS,aAAQ/T,KAAK+T,MARd;AASC1S,YAAOrB,KAAKqB,KATb;AAUCwS,gBAAW7T,KAAK6T,SAVjB;AAWCC,cAAS9T,KAAK8T;AAXf,KALD,CADD;AAmBA;AACD;;AAED,WAASrI,IAAT,GAAgB;AACfzL,QAAKH,OAAL,GAAe2L,GAAG/G,MAAH,CAAU6D,IAAV,CAAetI,KAAKsI,IAApB,EACb1C,IADa,CACR,WADQ,EACK5F,KAAKmK,SAAL,CAAetH,IAAf,CAAoBmB,EADzB,EAEb4B,IAFa,CAER,SAFQ,EAEG5F,KAAKoK,OAAL,CAAavH,IAAb,CAAkBmB,EAFrB,CAAf;AAGA;;AAED,WAASkP,MAAT,GAAkB;AAAE;AACnB5Q;AACAtC,QAAKH,OAAL,CAAa+F,IAAb,CAAkB,GAAlB,EAAuB5F,KAAKsI,IAA5B;AACA;AACDtI,OAAKkT,MAAL,GAAcA,MAAd;;AAEAjT;;AAEA,SAAOD,IAAP;AACA;;;;;;;ACxGD;AACA;AACA;AACA;AACA,EAAC,qBAAqB;;AAEtB;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA,IAAG;AACH;AACA;AACA;AACA;AACA,IAAG;AACH;AACA;AACA,GAAE;AACF;AACA;;AAEA;AACA;AACA;AACA,oCAAmC;AACnC;;AAEA;AACA;AACA,GAAE;AACF;AACA;AACA,GAAE;AACF;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAE,IAAI;AACN;;AAEA;;AAEA;;AAEA,EAAC","file":"frappe-gantt.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"Gantt\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Gantt\"] = factory();\n\telse\n\t\troot[\"Gantt\"] = factory();\n})(this, function() {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 40f9d74a4b17f4d126f3\n **/","/* global moment, Snap */\n/**\n * Gantt:\n * \telement: querySelector string, HTML DOM or SVG DOM element, required\n * \ttasks: array of tasks, required\n * task: { id, name, start, end, progress, dependencies, custom_class }\n * \tconfig: configuration options, optional\n */\nimport './gantt.scss';\n\nimport Bar from './Bar';\nimport Arrow from './Arrow';\n\nexport default function Gantt(element, tasks, config = {}) {\n\n\tconst self = {};\n\n\tfunction init() {\n\t\tset_defaults();\n\n\t\t// expose methods\n\t\tself.change_view_mode = change_view_mode;\n\t\tself.unselect_all = unselect_all;\n\t\tself.view_is = view_is;\n\t\tself.get_bar = get_bar;\n\t\tself.trigger_event = trigger_event;\n\t\tself.refresh = refresh;\n\n\t\t// initialize with default view mode\n\t\tchange_view_mode(self.config.view_mode);\n\t}\n\n\tfunction set_defaults() {\n\n\t\tconst merge = require('deepmerge');\n\n\t\tconst defaults = {\n\t\t\theader_height: 50,\n\t\t\tcolumn_width: 30,\n\t\t\tstep: 24,\n\t\t\tview_modes: [\n\t\t\t\t'Quarter Day',\n\t\t\t\t'Half Day',\n\t\t\t\t'Day',\n\t\t\t\t'Week',\n\t\t\t\t'Month'\n\t\t\t],\n\t\t\tbar: {\n\t\t\t\theight: 20,\n\t\t\t\tcorner_radius: 3\n\t\t\t},\n\t\t\tarrow: {\n\t\t\t\tcurve: 5\n\t\t\t},\n\t\t\tpadding: 18,\n\t\t\tview_mode: 'Day',\n\t\t\tdate_format: 'YYYY-MM-DD',\n\t\t\tcustom_popup_html: null\n\t\t};\n\t\tself.config = merge(defaults, config);\n\n\t\treset_variables(tasks);\n\t}\n\n\tfunction reset_variables(tasks) {\n\t\tif(typeof element === 'string') {\n\t\t\tself.element = document.querySelector(element);\n\t\t} else if (element instanceof SVGElement) {\n\t\t\tself.element = element;\n\t\t} else if (element instanceof HTMLElement) {\n\t\t\tself.element = element.querySelector('svg');\n\t\t} else {\n\t\t\tthrow new TypeError('Frappé Gantt only supports usage of a string CSS selector,' +\n\t\t\t\t' HTML DOM element or SVG DOM element for the \\'element\\' parameter');\n\t\t}\n\n\t\tself._tasks = tasks;\n\n\t\tself._bars = [];\n\t\tself._arrows = [];\n\t\tself.element_groups = {};\n\t}\n\n\tfunction refresh(updated_tasks) {\n\t\treset_variables(updated_tasks);\n\t\tchange_view_mode(self.config.view_mode);\n\t}\n\n\tfunction change_view_mode(mode) {\n\t\tset_scale(mode);\n\t\tprepare();\n\t\trender();\n\t\t// fire viewmode_change event\n\t\ttrigger_event('view_change', [mode]);\n\t}\n\n\tfunction prepare() {\n\t\tprepare_tasks();\n\t\tprepare_dependencies();\n\t\tprepare_dates();\n\t\tprepare_canvas();\n\t}\n\n\tfunction prepare_tasks() {\n\n\t\t// prepare tasks\n\t\tself.tasks = self._tasks.map((task, i) => {\n\n\t\t\t// momentify\n\t\t\ttask._start = moment(task.start, self.config.date_format);\n\t\t\ttask._end = moment(task.end, self.config.date_format);\n\n\t\t\t// make task invalid if duration too large\n\t\t\tif(task._end.diff(task._start, 'years') > 10) {\n\t\t\t\ttask.end = null;\n\t\t\t}\n\n\t\t\t// cache index\n\t\t\ttask._index = i;\n\n\t\t\t// invalid dates\n\t\t\tif(!task.start && !task.end) {\n\t\t\t\ttask._start = moment().startOf('day');\n\t\t\t\ttask._end = moment().startOf('day').add(2, 'days');\n\t\t\t}\n\t\t\tif(!task.start && task.end) {\n\t\t\t\ttask._start = task._end.clone().add(-2, 'days');\n\t\t\t}\n\t\t\tif(task.start && !task.end) {\n\t\t\t\ttask._end = task._start.clone().add(2, 'days');\n\t\t\t}\n\n\t\t\t// invalid flag\n\t\t\tif(!task.start || !task.end) {\n\t\t\t\ttask.invalid = true;\n\t\t\t}\n\n\t\t\t// dependencies\n\t\t\tif(typeof task.dependencies === 'string' || !task.dependencies) {\n\t\t\t\tlet deps = [];\n\t\t\t\tif(task.dependencies) {\n\t\t\t\t\tdeps = task.dependencies\n\t\t\t\t\t\t.split(',')\n\t\t\t\t\t\t.map(d => d.trim())\n\t\t\t\t\t\t.filter((d) => d);\n\t\t\t\t}\n\t\t\t\ttask.dependencies = deps;\n\t\t\t}\n\n\t\t\t// uids\n\t\t\tif(!task.id) {\n\t\t\t\ttask.id = generate_id(task);\n\t\t\t}\n\n\t\t\treturn task;\n\t\t});\n\t}\n\n\tfunction prepare_dependencies() {\n\n\t\tself.dependency_map = {};\n\t\tfor(let t of self.tasks) {\n\t\t\tfor(let d of t.dependencies) {\n\t\t\t\tself.dependency_map[d] = self.dependency_map[d] || [];\n\t\t\t\tself.dependency_map[d].push(t.id);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction prepare_dates() {\n\n\t\tself.gantt_start = self.gantt_end = null;\n\t\tfor(let task of self.tasks) {\n\t\t\t// set global start and end date\n\t\t\tif(!self.gantt_start || task._start < self.gantt_start) {\n\t\t\t\tself.gantt_start = task._start;\n\t\t\t}\n\t\t\tif(!self.gantt_end || task._end > self.gantt_end) {\n\t\t\t\tself.gantt_end = task._end;\n\t\t\t}\n\t\t}\n\t\tset_gantt_dates();\n\t\tsetup_dates();\n\t}\n\n\tfunction prepare_canvas() {\n\t\tif(self.canvas) return;\n\t\tself.canvas = Snap(self.element).addClass('gantt');\n\t}\n\n\tfunction render() {\n\t\tclear();\n\t\tsetup_groups();\n\t\tmake_grid();\n\t\tmake_dates();\n\t\tmake_bars();\n\t\tmake_arrows();\n\t\tmap_arrows_on_bars();\n\t\tset_width();\n\t\tset_scroll_position();\n\t\tbind_grid_click();\n\t}\n\n\tfunction clear() {\n\t\tself.canvas.clear();\n\t\tself._bars = [];\n\t\tself._arrows = [];\n\t}\n\n\tfunction set_gantt_dates() {\n\n\t\tif(view_is(['Quarter Day', 'Half Day'])) {\n\t\t\tself.gantt_start = self.gantt_start.clone().subtract(7, 'day');\n\t\t\tself.gantt_end = self.gantt_end.clone().add(7, 'day');\n\t\t} else if(view_is('Month')) {\n\t\t\tself.gantt_start = self.gantt_start.clone().startOf('year');\n\t\t\tself.gantt_end = self.gantt_end.clone().endOf('month').add(1, 'year');\n\t\t} else {\n\t\t\tself.gantt_start = self.gantt_start.clone().startOf('month').subtract(1, 'month');\n\t\t\tself.gantt_end = self.gantt_end.clone().endOf('month').add(1, 'month');\n\t\t}\n\t}\n\n\tfunction setup_dates() {\n\n\t\tself.dates = [];\n\t\tlet cur_date = null;\n\n\t\twhile(cur_date === null || cur_date < self.gantt_end) {\n\t\t\tif(!cur_date) {\n\t\t\t\tcur_date = self.gantt_start.clone();\n\t\t\t} else {\n\t\t\t\tcur_date = view_is('Month') ?\n\t\t\t\t\tcur_date.clone().add(1, 'month') :\n\t\t\t\t\tcur_date.clone().add(self.config.step, 'hours');\n\t\t\t}\n\t\t\tself.dates.push(cur_date);\n\t\t}\n\t}\n\n\tfunction setup_groups() {\n\n\t\tconst groups = ['grid', 'date', 'arrow', 'progress', 'bar', 'details'];\n\t\t// make group layers\n\t\tfor(let group of groups) {\n\t\t\tself.element_groups[group] = self.canvas.group().attr({'id': group});\n\t\t}\n\t}\n\n\tfunction set_scale(scale) {\n\t\tself.config.view_mode = scale;\n\n\t\tif(scale === 'Day') {\n\t\t\tself.config.step = 24;\n\t\t\tself.config.column_width = 38;\n\t\t} else if(scale === 'Half Day') {\n\t\t\tself.config.step = 24 / 2;\n\t\t\tself.config.column_width = 38;\n\t\t} else if(scale === 'Quarter Day') {\n\t\t\tself.config.step = 24 / 4;\n\t\t\tself.config.column_width = 38;\n\t\t} else if(scale === 'Week') {\n\t\t\tself.config.step = 24 * 7;\n\t\t\tself.config.column_width = 140;\n\t\t} else if(scale === 'Month') {\n\t\t\tself.config.step = 24 * 30;\n\t\t\tself.config.column_width = 120;\n\t\t}\n\t}\n\n\tfunction set_width() {\n\t\tconst cur_width = self.canvas.node.getBoundingClientRect().width;\n\t\tconst actual_width = self.canvas.select('#grid .grid-row').attr('width');\n\t\tif(cur_width < actual_width) {\n\t\t\tself.canvas.attr('width', actual_width);\n\t\t}\n\t}\n\n\tfunction set_scroll_position() {\n\t\tconst parent_element = self.element.parentElement;\n\n\t\tif(!parent_element) return;\n\n\t\tconst scroll_pos = get_min_date().diff(self.gantt_start, 'hours') /\n\t\t\tself.config.step * self.config.column_width - self.config.column_width;\n\t\tparent_element.scrollLeft = scroll_pos;\n\t}\n\n\tfunction get_min_date() {\n\t\tconst task = self.tasks.reduce((acc, curr) => {\n\t\t\treturn curr._start.isSameOrBefore(acc._start) ? curr : acc;\n\t\t});\n\t\treturn task._start;\n\t}\n\n\tfunction make_grid() {\n\t\tmake_grid_background();\n\t\tmake_grid_rows();\n\t\tmake_grid_header();\n\t\tmake_grid_ticks();\n\t\tmake_grid_highlights();\n\t}\n\n\tfunction make_grid_background() {\n\n\t\tconst grid_width = self.dates.length * self.config.column_width,\n\t\t\tgrid_height = self.config.header_height + self.config.padding +\n\t\t\t\t(self.config.bar.height + self.config.padding) * self.tasks.length;\n\n\t\tself.canvas.rect(0, 0, grid_width, grid_height)\n\t\t\t.addClass('grid-background')\n\t\t\t.appendTo(self.element_groups.grid);\n\n\t\tself.canvas.attr({\n\t\t\theight: grid_height + self.config.padding + 100,\n\t\t\twidth: '100%'\n\t\t});\n\t}\n\n\tfunction make_grid_header() {\n\t\tconst header_width = self.dates.length * self.config.column_width,\n\t\t\theader_height = self.config.header_height + 10;\n\t\tself.canvas.rect(0, 0, header_width, header_height)\n\t\t\t.addClass('grid-header')\n\t\t\t.appendTo(self.element_groups.grid);\n\t}\n\n\tfunction make_grid_rows() {\n\n\t\tconst rows = self.canvas.group().appendTo(self.element_groups.grid),\n\t\t\tlines = self.canvas.group().appendTo(self.element_groups.grid),\n\t\t\trow_width = self.dates.length * self.config.column_width,\n\t\t\trow_height = self.config.bar.height + self.config.padding;\n\n\t\tlet row_y = self.config.header_height + self.config.padding / 2;\n\n\t\tfor(let task of self.tasks) { // eslint-disable-line\n\t\t\tself.canvas.rect(0, row_y, row_width, row_height)\n\t\t\t\t.addClass('grid-row')\n\t\t\t\t.appendTo(rows);\n\n\t\t\tself.canvas.line(0, row_y + row_height, row_width, row_y + row_height)\n\t\t\t\t.addClass('row-line')\n\t\t\t\t.appendTo(lines);\n\n\t\t\trow_y += self.config.bar.height + self.config.padding;\n\t\t}\n\t}\n\n\tfunction make_grid_ticks() {\n\t\tlet tick_x = 0,\n\t\t\ttick_y = self.config.header_height + self.config.padding / 2,\n\t\t\ttick_height = (self.config.bar.height + self.config.padding) * self.tasks.length;\n\n\t\tfor(let date of self.dates) {\n\t\t\tlet tick_class = 'tick';\n\t\t\t// thick tick for monday\n\t\t\tif(view_is('Day') && date.day() === 1) {\n\t\t\t\ttick_class += ' thick';\n\t\t\t}\n\t\t\t// thick tick for first week\n\t\t\tif(view_is('Week') && date.date() >= 1 && date.date() < 8) {\n\t\t\t\ttick_class += ' thick';\n\t\t\t}\n\t\t\t// thick ticks for quarters\n\t\t\tif(view_is('Month') && date.month() % 3 === 0) {\n\t\t\t\ttick_class += ' thick';\n\t\t\t}\n\n\t\t\tself.canvas.path(Snap.format('M {x} {y} v {height}', {\n\t\t\t\tx: tick_x,\n\t\t\t\ty: tick_y,\n\t\t\t\theight: tick_height\n\t\t\t}))\n\t\t\t.addClass(tick_class)\n\t\t\t.appendTo(self.element_groups.grid);\n\n\t\t\tif(view_is('Month')) {\n\t\t\t\ttick_x += date.daysInMonth() * self.config.column_width / 30;\n\t\t\t} else {\n\t\t\t\ttick_x += self.config.column_width;\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction make_grid_highlights() {\n\n\t\t// highlight today's date\n\t\tif(view_is('Day')) {\n\t\t\tconst x = moment().startOf('day').diff(self.gantt_start, 'hours') /\n\t\t\t\t\tself.config.step * self.config.column_width;\n\t\t\tconst y = 0;\n\t\t\tconst width = self.config.column_width;\n\t\t\tconst height = (self.config.bar.height + self.config.padding) * self.tasks.length +\n\t\t\t\tself.config.header_height + self.config.padding / 2;\n\n\t\t\tself.canvas.rect(x, y, width, height)\n\t\t\t\t.addClass('today-highlight')\n\t\t\t\t.appendTo(self.element_groups.grid);\n\t\t}\n\t}\n\n\tfunction make_dates() {\n\n\t\tfor(let date of get_dates_to_draw()) {\n\t\t\tself.canvas.text(date.lower_x, date.lower_y, date.lower_text)\n\t\t\t\t.addClass('lower-text')\n\t\t\t\t.appendTo(self.element_groups.date);\n\n\t\t\tif(date.upper_text) {\n\t\t\t\tconst $upper_text = self.canvas.text(date.upper_x, date.upper_y, date.upper_text)\n\t\t\t\t\t.addClass('upper-text')\n\t\t\t\t\t.appendTo(self.element_groups.date);\n\n\t\t\t\t// remove out-of-bound dates\n\t\t\t\tif($upper_text.getBBox().x2 > self.element_groups.grid.getBBox().width) {\n\t\t\t\t\t$upper_text.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction get_dates_to_draw() {\n\t\tlet last_date = null;\n\t\tconst dates = self.dates.map((date, i) => {\n\t\t\tconst d = get_date_info(date, last_date, i);\n\t\t\tlast_date = date;\n\t\t\treturn d;\n\t\t});\n\t\treturn dates;\n\t}\n\n\tfunction get_date_info(date, last_date, i) {\n\t\tif(!last_date) {\n\t\t\tlast_date = date.clone().add(1, 'year');\n\t\t}\n\t\tconst date_text = {\n\t\t\t'Quarter Day_lower': date.format('HH'),\n\t\t\t'Half Day_lower': date.format('HH'),\n\t\t\t'Day_lower': date.date() !== last_date.date() ? date.format('D') : '',\n\t\t\t'Week_lower': date.month() !== last_date.month() ?\n\t\t\t\tdate.format('D MMM') : date.format('D'),\n\t\t\t'Month_lower': date.format('MMMM'),\n\t\t\t'Quarter Day_upper': date.date() !== last_date.date() ? date.format('D MMM') : '',\n\t\t\t'Half Day_upper': date.date() !== last_date.date() ?\n\t\t\t\tdate.month() !== last_date.month() ?\n\t\t\t\tdate.format('D MMM') : date.format('D') : '',\n\t\t\t'Day_upper': date.month() !== last_date.month() ? date.format('MMMM') : '',\n\t\t\t'Week_upper': date.month() !== last_date.month() ? date.format('MMMM') : '',\n\t\t\t'Month_upper': date.year() !== last_date.year() ? date.format('YYYY') : ''\n\t\t};\n\n\t\tconst base_pos = {\n\t\t\tx: i * self.config.column_width,\n\t\t\tlower_y: self.config.header_height,\n\t\t\tupper_y: self.config.header_height - 25\n\t\t};\n\n\t\tconst x_pos = {\n\t\t\t'Quarter Day_lower': (self.config.column_width * 4) / 2,\n\t\t\t'Quarter Day_upper': 0,\n\t\t\t'Half Day_lower': (self.config.column_width * 2) / 2,\n\t\t\t'Half Day_upper': 0,\n\t\t\t'Day_lower': self.config.column_width / 2,\n\t\t\t'Day_upper': (self.config.column_width * 30) / 2,\n\t\t\t'Week_lower': 0,\n\t\t\t'Week_upper': (self.config.column_width * 4) / 2,\n\t\t\t'Month_lower': self.config.column_width / 2,\n\t\t\t'Month_upper': (self.config.column_width * 12) / 2\n\t\t};\n\n\t\treturn {\n\t\t\tupper_text: date_text[`${self.config.view_mode}_upper`],\n\t\t\tlower_text: date_text[`${self.config.view_mode}_lower`],\n\t\t\tupper_x: base_pos.x + x_pos[`${self.config.view_mode}_upper`],\n\t\t\tupper_y: base_pos.upper_y,\n\t\t\tlower_x: base_pos.x + x_pos[`${self.config.view_mode}_lower`],\n\t\t\tlower_y: base_pos.lower_y\n\t\t};\n\t}\n\n\tfunction make_arrows() {\n\t\tself._arrows = [];\n\t\tfor(let task of self.tasks) {\n\t\t\tlet arrows = [];\n\t\t\tarrows = task.dependencies.map(dep => {\n\t\t\t\tconst dependency = get_task(dep);\n\t\t\t\tif(!dependency) return;\n\n\t\t\t\tconst arrow = Arrow(\n\t\t\t\t\tself, // gt\n\t\t\t\t\tself._bars[dependency._index], // from_task\n\t\t\t\t\tself._bars[task._index] // to_task\n\t\t\t\t);\n\t\t\t\tself.element_groups.arrow.add(arrow.element);\n\t\t\t\treturn arrow; // eslint-disable-line\n\t\t\t}).filter(arr => arr); // filter falsy values\n\t\t\tself._arrows = self._arrows.concat(arrows);\n\t\t}\n\t}\n\n\tfunction make_bars() {\n\n\t\tself._bars = self.tasks.map((task) => {\n\t\t\tconst bar = Bar(self, task);\n\t\t\tself.element_groups.bar.add(bar.group);\n\t\t\treturn bar;\n\t\t});\n\t}\n\n\tfunction map_arrows_on_bars() {\n\t\tfor(let bar of self._bars) {\n\t\t\tbar.arrows = self._arrows.filter(arrow => {\n\t\t\t\treturn (arrow.from_task.task.id === bar.task.id) ||\n\t\t\t\t\t(arrow.to_task.task.id === bar.task.id);\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction bind_grid_click() {\n\t\tself.element_groups.grid.click(() => {\n\t\t\tunselect_all();\n\t\t\tself.element_groups.details\n\t\t\t\t.selectAll('.details-wrapper')\n\t\t\t\t.forEach(el => el.addClass('hide'));\n\t\t});\n\t}\n\n\tfunction unselect_all() {\n\t\tself.canvas.selectAll('.bar-wrapper').forEach(el => {\n\t\t\tel.removeClass('active');\n\t\t});\n\t}\n\n\tfunction view_is(modes) {\n\t\tif (typeof modes === 'string') {\n\t\t\treturn self.config.view_mode === modes;\n\t\t} else if(Array.isArray(modes)) {\n\t\t\tfor (let mode of modes) {\n\t\t\t\tif(self.config.view_mode === mode) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction get_task(id) {\n\t\treturn self.tasks.find((task) => {\n\t\t\treturn task.id === id;\n\t\t});\n\t}\n\n\tfunction get_bar(id) {\n\t\treturn self._bars.find((bar) => {\n\t\t\treturn bar.task.id === id;\n\t\t});\n\t}\n\n\tfunction generate_id(task) {\n\t\treturn task.name + '_' + Math.random().toString(36).slice(2, 12);\n\t}\n\n\tfunction trigger_event(event, args) {\n\t\tif(self.config['on_' + event]) {\n\t\t\tself.config['on_' + event].apply(null, args);\n\t\t}\n\t}\n\n\tinit();\n\n\treturn self;\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./~/eslint-loader!./src/Gantt.js\n **/","// style-loader: Adds some css to the DOM by adding a + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/autolayout.html b/static/mxgraph/examples/autolayout.html new file mode 100644 index 0000000..bd64a5e --- /dev/null +++ b/static/mxgraph/examples/autolayout.html @@ -0,0 +1,198 @@ + + + + Auto layout example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/boundary.html b/static/mxgraph/examples/boundary.html new file mode 100644 index 0000000..4857b18 --- /dev/null +++ b/static/mxgraph/examples/boundary.html @@ -0,0 +1,220 @@ + + + + Boundary example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/clipboard.html b/static/mxgraph/examples/clipboard.html new file mode 100644 index 0000000..d0b87d0 --- /dev/null +++ b/static/mxgraph/examples/clipboard.html @@ -0,0 +1,355 @@ + + + + Clipboard example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/codec.html b/static/mxgraph/examples/codec.html new file mode 100644 index 0000000..60f0f20 --- /dev/null +++ b/static/mxgraph/examples/codec.html @@ -0,0 +1,161 @@ + + + + Codec example for mxGraph + + + + + + + + + + + + + This graph is embedded in the page. +
<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" vertex="1" parent="1" value="Interval 1"><mxGeometry x="380" y="0" width="140" height="30" as="geometry"/></mxCell><mxCell id="3" vertex="1" parent="1" value="Interval 2"><mxGeometry x="200" y="80" width="380" height="30" as="geometry"/></mxCell><mxCell id="4" vertex="1" parent="1" value="Interval 3"><mxGeometry x="40" y="140" width="260" height="30" as="geometry"/></mxCell><mxCell id="5" vertex="1" parent="1" value="Interval 4"><mxGeometry x="120" y="200" width="240" height="30" as="geometry"/></mxCell><mxCell id="6" vertex="1" parent="1" value="Interval 5"><mxGeometry x="420" y="260" width="80" height="30" as="geometry"/></mxCell><mxCell id="7" edge="1" source="2" target="3" parent="1" value="Transfer1"><mxGeometry as="geometry"><Array as="points"><Object x="420" y="60"/></Array></mxGeometry></mxCell><mxCell id="8" edge="1" source="2" target="6" parent="1" value=""><mxGeometry as="geometry" relative="1" y="-30"><Array as="points"><Object x="600" y="60"/></Array></mxGeometry></mxCell><mxCell id="9" edge="1" source="3" target="4" parent="1" value="Transfer3"><mxGeometry as="geometry"><Array as="points"><Object x="260" y="120"/></Array></mxGeometry></mxCell><mxCell id="10" edge="1" source="4" target="5" parent="1" value="Transfer4"><mxGeometry as="geometry"><Array as="points"><Object x="200" y="180"/></Array></mxGeometry></mxCell><mxCell id="11" edge="1" source="4" target="6" parent="1" value="Transfer5"><mxGeometry as="geometry" relative="1" y="-10"><Array as="points"><Object x="460" y="155"/></Array></mxGeometry></mxCell></root></mxGraphModel>
+ This graph is embedded in the page. +
<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" vertex="1" parent="1" value="Interval 1"><mxGeometry x="380" y="0" width="140" height="30" as="geometry"/></mxCell><mxCell id="3" vertex="1" parent="1" value="Interval 2"><mxGeometry x="200" y="80" width="380" height="30" as="geometry"/></mxCell><mxCell id="4" vertex="1" parent="1" value="Interval 3"><mxGeometry x="40" y="140" width="260" height="30" as="geometry"/></mxCell><mxCell id="5" vertex="1" parent="1" value="Interval 4"><mxGeometry x="120" y="200" width="240" height="30" as="geometry"/></mxCell><mxCell id="6" vertex="1" parent="1" value="Interval 5"><mxGeometry x="420" y="260" width="80" height="30" as="geometry"/></mxCell><mxCell id="7" edge="1" source="2" target="3" parent="1" value="Transfer1"><mxGeometry as="geometry"><Array as="points"><Object x="420" y="60"/></Array></mxGeometry></mxCell><mxCell id="8" edge="1" source="2" target="6" parent="1" value=""><mxGeometry as="geometry" relative="1" y="-30"><Array as="points"><Object x="600" y="60"/></Array></mxGeometry></mxCell><mxCell id="9" edge="1" source="3" target="4" parent="1" value="Transfer3"><mxGeometry as="geometry"><Array as="points"><Object x="260" y="120"/></Array></mxGeometry></mxCell><mxCell id="10" edge="1" source="4" target="5" parent="1" value="Transfer4"><mxGeometry as="geometry"><Array as="points"><Object x="200" y="180"/></Array></mxGeometry></mxCell><mxCell id="11" edge="1" source="4" target="6" parent="1" value="Transfer5"><mxGeometry as="geometry" relative="1" y="-10"><Array as="points"><Object x="460" y="155"/></Array></mxGeometry></mxCell></root></mxGraphModel>
+ This graph is embedded in the page. +
<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" vertex="1" parent="1" value="Interval 1"><mxGeometry x="380" y="20" width="140" height="30" as="geometry"/></mxCell><mxCell id="3" vertex="1" parent="1" value="Interval 2"><mxGeometry x="200" y="80" width="380" height="30" as="geometry"/></mxCell><mxCell id="4" vertex="1" parent="1" value="Interval 3"><mxGeometry x="40" y="140" width="260" height="30" as="geometry"/></mxCell><mxCell id="5" vertex="1" parent="1" value="Interval 4"><mxGeometry x="120" y="200" width="240" height="30" as="geometry"/></mxCell><mxCell id="6" vertex="1" parent="1" value="Interval 5"><mxGeometry x="420" y="260" width="80" height="30" as="geometry"/></mxCell><mxCell id="7" edge="1" source="2" target="3" parent="1" value="Transfer1"><mxGeometry as="geometry"><Array as="points"><Object x="420" y="60"/></Array></mxGeometry></mxCell><mxCell id="8" edge="1" source="2" target="6" parent="1" value="Transfer2"><mxGeometry as="geometry" relative="1" y="0"><Array as="points"><Object x="600" y="60"/></Array></mxGeometry></mxCell><mxCell id="9" edge="1" source="3" target="4" parent="1" value="Transfer3"><mxGeometry as="geometry"><Array as="points"><Object x="260" y="120"/></Array></mxGeometry></mxCell><mxCell id="10" edge="1" source="4" target="5" parent="1" value="Transfer4"><mxGeometry as="geometry"><Array as="points"><Object x="200" y="180"/></Array></mxGeometry></mxCell><mxCell id="11" edge="1" source="4" target="6" parent="1" value="Transfer5"><mxGeometry as="geometry" relative="1" y="-10"><Array as="points"><Object x="460" y="155"/></Array></mxGeometry></mxCell></root></mxGraphModel>
+ This graph is embedded in the page. +
<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" vertex="1" parent="1" value="Interval 1"><mxGeometry x="380" y="20" width="140" height="30" as="geometry"/></mxCell><mxCell id="3" vertex="1" parent="1" value="Interval 2"><mxGeometry x="200" y="80" width="380" height="30" as="geometry"/></mxCell><mxCell id="4" vertex="1" parent="1" value="Interval 3"><mxGeometry x="40" y="140" width="260" height="30" as="geometry"/></mxCell><mxCell id="5" vertex="1" parent="1" value="Interval 4"><mxGeometry x="120" y="200" width="240" height="30" as="geometry"/></mxCell><mxCell id="6" vertex="1" parent="1" value="Interval 5"><mxGeometry x="420" y="260" width="80" height="30" as="geometry"/></mxCell><mxCell id="7" edge="1" source="2" target="3" parent="1" value="Transfer1"><mxGeometry as="geometry"><Array as="points"><Object x="420" y="60"/></Array></mxGeometry></mxCell><mxCell id="8" edge="1" source="2" target="6" parent="1" value="Transfer2"><mxGeometry as="geometry" relative="1" y="0"><Array as="points"><Object x="600" y="60"/></Array></mxGeometry></mxCell><mxCell id="9" edge="1" source="3" target="4" parent="1" value="Transfer3"><mxGeometry as="geometry"><Array as="points"><Object x="260" y="120"/></Array></mxGeometry></mxCell><mxCell id="10" edge="1" source="4" target="5" parent="1" value="Transfer4"><mxGeometry as="geometry"><Array as="points"><Object x="200" y="180"/></Array></mxGeometry></mxCell><mxCell id="11" edge="1" source="4" target="6" parent="1" value="Transfer5"><mxGeometry as="geometry" relative="1" y="-10"><Array as="points"><Object x="460" y="155"/></Array></mxGeometry></mxCell></root></mxGraphModel>
+ This graph is embedded in the page. + + + diff --git a/static/mxgraph/examples/collapse.html b/static/mxgraph/examples/collapse.html new file mode 100644 index 0000000..7652a6e --- /dev/null +++ b/static/mxgraph/examples/collapse.html @@ -0,0 +1,73 @@ + + + + Collapse example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/constituent.html b/static/mxgraph/examples/constituent.html new file mode 100644 index 0000000..889e01d --- /dev/null +++ b/static/mxgraph/examples/constituent.html @@ -0,0 +1,108 @@ + + + + Consistuent example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/contexticons.html b/static/mxgraph/examples/contexticons.html new file mode 100644 index 0000000..69221dc --- /dev/null +++ b/static/mxgraph/examples/contexticons.html @@ -0,0 +1,238 @@ + + + + Context icons example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/control.html b/static/mxgraph/examples/control.html new file mode 100644 index 0000000..de0f89a --- /dev/null +++ b/static/mxgraph/examples/control.html @@ -0,0 +1,177 @@ + + + + Control example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/dragsource.html b/static/mxgraph/examples/dragsource.html new file mode 100644 index 0000000..a5bf615 --- /dev/null +++ b/static/mxgraph/examples/dragsource.html @@ -0,0 +1,204 @@ + + + + Dragsource example for mxGraph + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/drop.html b/static/mxgraph/examples/drop.html new file mode 100644 index 0000000..300c3da --- /dev/null +++ b/static/mxgraph/examples/drop.html @@ -0,0 +1,178 @@ + + + + Drop example for mxGraph + + + + + + + + + + + + + + Drag & drop your images below:
+ +
+
+ + diff --git a/static/mxgraph/examples/dynamicloading.html b/static/mxgraph/examples/dynamicloading.html new file mode 100644 index 0000000..a977958 --- /dev/null +++ b/static/mxgraph/examples/dynamicloading.html @@ -0,0 +1,247 @@ + + + + Dynamic loading example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/dynamicstyle.html b/static/mxgraph/examples/dynamicstyle.html new file mode 100644 index 0000000..af82c50 --- /dev/null +++ b/static/mxgraph/examples/dynamicstyle.html @@ -0,0 +1,122 @@ + + + + Dynamic Style example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/dynamictoolbar.html b/static/mxgraph/examples/dynamictoolbar.html new file mode 100644 index 0000000..115dc47 --- /dev/null +++ b/static/mxgraph/examples/dynamictoolbar.html @@ -0,0 +1,167 @@ + + + + Toolbar example for mxGraph + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/edgetolerance.html b/static/mxgraph/examples/edgetolerance.html new file mode 100644 index 0000000..a605b73 --- /dev/null +++ b/static/mxgraph/examples/edgetolerance.html @@ -0,0 +1,124 @@ + + + + Edge tolerance example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/editing.html b/static/mxgraph/examples/editing.html new file mode 100644 index 0000000..bcec192 --- /dev/null +++ b/static/mxgraph/examples/editing.html @@ -0,0 +1,164 @@ + + + + Editing example for mxGraph + + + + + + + + + + + + + +

+ Double-click the upper/lower half of the cell to edit different fields of the user object. +

+ +
+
+ + diff --git a/static/mxgraph/examples/editors/config/diagrameditor.xml b/static/mxgraph/examples/editors/config/diagrameditor.xml new file mode 100644 index 0000000..8d5a2fb --- /dev/null +++ b/static/mxgraph/examples/editors/config/diagrameditor.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + label + + ' (' + cell.getId() + ')
') : '') + + ((style != null) ? ('
Style: ' + style + '
') : '') + + 'Connections: ' + cell.getEdgeCount()+ + '
Children: ' + cell.getChildCount(); + } + ]]>
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

+ + + + + + + + + + + + + + +
+
diff --git a/static/mxgraph/examples/editors/config/editor-commons.xml b/static/mxgraph/examples/editors/config/editor-commons.xml new file mode 100644 index 0000000..420e2cd --- /dev/null +++ b/static/mxgraph/examples/editors/config/editor-commons.xml @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) + { + window.open(href); + } + else + { + mxUtils.alert('No URL defined. Showing properties...'); + editor.execute('showProperties', cell); + } + } + } + ]]> + + + + + + 0) + { + editor.graph.setCellStyles("fontFamily", family); + } + } + ]]> + 0 && size < 999) + { + editor.graph.setCellStyles("fontSize", size); + } + } + ]]> + + = 0 && opacity <= 100) + { + editor.graph.setCellStyles("opacity", opacity); + } + } + ]]> + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/config/keyhandler-commons.xml b/static/mxgraph/examples/editors/config/keyhandler-commons.xml new file mode 100644 index 0000000..1e2c159 --- /dev/null +++ b/static/mxgraph/examples/editors/config/keyhandler-commons.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/config/keyhandler-minimal.xml b/static/mxgraph/examples/editors/config/keyhandler-minimal.xml new file mode 100644 index 0000000..7f4ce3c --- /dev/null +++ b/static/mxgraph/examples/editors/config/keyhandler-minimal.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/config/layouteditor.xml b/static/mxgraph/examples/editors/config/layouteditor.xml new file mode 100644 index 0000000..09f3a09 --- /dev/null +++ b/static/mxgraph/examples/editors/config/layouteditor.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/config/processeditor.xml b/static/mxgraph/examples/editors/config/processeditor.xml new file mode 100644 index 0000000..7beeb67 --- /dev/null +++ b/static/mxgraph/examples/editors/config/processeditor.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0; + } + ]]> + + function (cell) + { + return mxUtils.isNode(this.model.getValue(cell), 'swimlane'); + } + + + function(cell) + { + return !this.isSwimlane(cell.parent); + } + + '+cell.getAttribute('label')+ + ' ('+cell.getId()+')'+ + '
Style: '+cell.getStyle()+ + '
Edges: '+cell.getEdgeCount()+ + '
Children: '+cell.getChildCount(); + } + ]]>
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/static/mxgraph/examples/editors/config/wfeditor-commons.xml b/static/mxgraph/examples/editors/config/wfeditor-commons.xml new file mode 100644 index 0000000..eea4da3 --- /dev/null +++ b/static/mxgraph/examples/editors/config/wfeditor-commons.xml @@ -0,0 +1,184 @@ + + + + function () + { + this.showTasks(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) || this.graph.isSwimlane(cell)) + { + mxUtils.para(div, 'Layout'); + mxUtils.linkAction(div, mxResources.get('verticalTree'), + this, 'verticalTree', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('horizontalTree'), + this, 'horizontalTree', off); + mxUtils.br(div); + } + + mxUtils.para(div, 'Format'); + + if (mxUtils.isNode(cell.value, 'Symbol')) + { + mxUtils.linkAction(div, mxResources.get('image'), + this, 'image', off); + mxUtils.br(div); + } + else + { + mxUtils.linkAction(div, mxResources.get('opacity'), + this, 'opacity', off); + mxUtils.br(div); + if (this.graph.model.isVertex(cell) || + (cell.style != null && + cell.style.indexOf("arrowEdge") >= 0)) + { + mxUtils.linkAction(div, mxResources.get('gradientColor'), + this, 'gradientColor', off); + mxUtils.br(div); + } + if (this.graph.model.isEdge(cell)) + { + mxUtils.linkAction(div, 'Straight Connector', this, 'straightConnector', off); + mxUtils.br(div); + mxUtils.linkAction(div, 'Elbow Connector', this, 'elbowConnector', off); + mxUtils.br(div); + mxUtils.linkAction(div, 'Arrow Connector', this, 'arrowConnector', off); + mxUtils.br(div); + } + } + + mxUtils.linkAction(div, 'Rounded', this, 'toggleRounded', off); + mxUtils.br(div); + if (this.graph.isSwimlane(cell) || this.graph.model.isEdge(cell)) + { + mxUtils.linkAction(div, 'Orientation', this, 'toggleOrientation', off); + mxUtils.br(div); + } + + if (this.graph.getSelectionCount() > 1) + { + mxUtils.para(div, mxResources.get('align')); + mxUtils.linkAction(div, mxResources.get('left'), + this, 'alignCellsLeft', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('center'), + this, 'alignCellsCenter', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('right'), + this, 'alignCellsRight', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('top'), + this, 'alignCellsTop', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('middle'), + this, 'alignCellsMiddle', off); + mxUtils.br(div); + mxUtils.linkAction(div, mxResources.get('bottom'), + this, 'alignCellsBottom', off); + mxUtils.br(div); + } + + mxUtils.para(div, mxResources.get('selection')); + mxUtils.linkAction(div, mxResources.get('clearSelection'), + this, 'selectNone', off); + mxUtils.br(div); + } + else if (layer.getChildCount() > 0) + { + mxUtils.para(div, mxResources.get('selection')); + mxUtils.linkAction(div, mxResources.get('selectAll'), + this, 'selectAll', off); + mxUtils.br(div); + } + + mxUtils.br(div); + } + } + ]]> + diff --git a/static/mxgraph/examples/editors/config/wfgraph-commons.xml b/static/mxgraph/examples/editors/config/wfgraph-commons.xml new file mode 100644 index 0000000..b18dc48 --- /dev/null +++ b/static/mxgraph/examples/editors/config/wfgraph-commons.xml @@ -0,0 +1,152 @@ + + + + + 0) ? + '
'+href : ''; + var maxlen = 30; + var desc = cell.getAttribute('description'); + if (desc == null || desc.length == 0) + { + desc = ''; + } + else if (desc.length < maxlen) + { + desc = '
'+desc; + } + else + { + desc = '
'+desc.substring(0, maxlen)+'...'; + } + return ''+cell.getAttribute('label')+ + ' ('+cell.getId()+')'+href+desc+ + '
Edges: '+cell.getEdgeCount()+ + '
Children: '+cell.getChildCount(); + } + ]]>
+ + function(cell) + { + return cell.getAttribute('label'); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/static/mxgraph/examples/editors/config/wftoolbar-commons.xml b/static/mxgraph/examples/editors/config/wftoolbar-commons.xml new file mode 100644 index 0000000..ca91e1e --- /dev/null +++ b/static/mxgraph/examples/editors/config/wftoolbar-commons.xml @@ -0,0 +1,74 @@ + + + + + +

+ + + + +

+ + +

+ + + + +

+ + +

+ + + + + + +

+ + + + + + + + + + + + + + + + + +

+ + + + + +

+ + + + +
diff --git a/static/mxgraph/examples/editors/config/workfloweditor.xml b/static/mxgraph/examples/editors/config/workfloweditor.xml new file mode 100644 index 0000000..846e7f3 --- /dev/null +++ b/static/mxgraph/examples/editors/config/workfloweditor.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/css/process.css b/static/mxgraph/examples/editors/css/process.css new file mode 100644 index 0000000..e5d01c0 --- /dev/null +++ b/static/mxgraph/examples/editors/css/process.css @@ -0,0 +1,3 @@ +img.mxToolbarMode { + margin-right: 7px; +} diff --git a/static/mxgraph/examples/editors/css/wordpress.css b/static/mxgraph/examples/editors/css/wordpress.css new file mode 100644 index 0000000..c6d4971 --- /dev/null +++ b/static/mxgraph/examples/editors/css/wordpress.css @@ -0,0 +1,599 @@ +/* Begin Typography & Colors */ +body { + font-size: 62.5%; /* Resets 1em to 10px */ + font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; + background: #d5d6d7 url('../images/draw/drawbgcolor.jpg'); + color: #333; + text-align: center; + } + +#page { + background-color: white; + border: 1px solid #959596; + text-align: left; + } + +#header { + background: #73a0c5 url('../images/draw/drawheader.jpg') no-repeat bottom center; + } + +#headerimg { + margin: 7px 9px 0; + height: 62px; + width: 740px; + } + +#content { + font-size: 1.2em + } + +.widecolumn .entry p { + font-size: 1.05em; + } + +.narrowcolumn .entry, .widecolumn .entry { + line-height: 1.4em; + } + +.widecolumn { + line-height: 1.6em; + } + +.narrowcolumn .postmetadata { + text-align: center; + } + +.alt { + background-color: #f8f8f8; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + } + +pre { + background: #f8f8f8; + font-size: 12px; + padding: 8px; +} + +#footer { + background: #eee url('../images/draw/drawfooter.jpg') no-repeat top; + border: none; + } + +small { + font-family: Arial, Helvetica, Sans-Serif; + font-size: 0.9em; + line-height: 1.5em; + } + +h1, h2, h3 { + font-family: 'Trebuchet MS', 'Lucida Grande', Verdana, Arial, Sans-Serif; + font-weight: bold; + } + +h1 { + font-size: 2em; + text-align: center; + } + +#headerimg .description { + font-size: 1.2em; + text-align: center; + } + +h2 { + font-size: 1.6em; + } + +h2.pagetitle { + font-size: 1.6em; + } + +#sidebar h2 { + font-family: 'Lucida Grande', Verdana, Sans-Serif; + font-size: 1.2em; + } + +h3 { + font-size: 1.3em; + } + +h1, h1 a, h1 a:hover, h1 a:visited, #headerimg .description { + text-decoration: none; + color: white; + } + +h2, h2 a, h2 a:visited, h3, h3 a, h3 a:visited { + color: #333; + } + +h2, h2 a, h2 a:hover, h2 a:visited, h3, h3 a, h3 a:hover, h3 a:visited, #sidebar h2, #wp-calendar caption, cite { + text-decoration: none; + } + +.entry p a:visited { + color: #b85b5a; + } + +.commentlist li, #commentform input, #commentform textarea { + font: 0.9em 'Lucida Grande', Verdana, Arial, Sans-Serif; + } + +.commentlist li { + font-weight: bold; + } + +.commentlist cite, .commentlist cite a { + font-weight: bold; + font-style: normal; + font-size: 1.1em; + } + +.commentlist p { + font-weight: normal; + line-height: 1.5em; + text-transform: none; + } + +#commentform p { + font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; + } + +.commentmetadata { + font-weight: normal; + } + +#sidebar { + font: 1em 'Lucida Grande', Verdana, Arial, Sans-Serif; + } + +small, #sidebar ul ul li, #sidebar ul ol li, .nocomments, .postmetadata, blockquote, strike { + color: #777; + } + +code { + font: 1.1em 'Courier New', Courier, Fixed; + } + +acronym, abbr, span.caps +{ + font-size: 0.9em; + letter-spacing: .07em; + } + +a, h2 a:hover, h3 a:hover { + color: #06c; + text-decoration: none; + } + +a:hover { + color: #147; + text-decoration: underline; + } + +#wp-calendar #prev a { + font-size: 9pt; + } + +#wp-calendar a { + text-decoration: none; + } + +#wp-calendar caption { + font: bold 1.3em 'Lucida Grande', Verdana, Arial, Sans-Serif; + text-align: center; + } + +#wp-calendar th { + font-style: normal; + text-transform: capitalize; + } +/* End Typography & Colors */ + + + +/* Begin Structure */ +body { + margin: 0 0 20px 0; + padding: 0; + } + +#page { + background-color: white; + margin: 20px auto; + padding: 0; + width: 760px; + border: 1px solid #959596; + } + +#header { + background-color: #73a0c5; + margin: 0 0 0 1px; + padding: 0; + height: 70px; + width: 758px; + } + +#headerimg { + margin: 0; + height: 70px; + width: 100%; + } + +.narrowcolumn { + float: left; + padding: 0 0 20px 45px; + margin: 0px 0 0; + width: 450px; + } + +.widecolumn { + padding: 10px 0 20px 0; + margin: 5px 0 0 150px; + width: 450px; + } + +.post { + margin: 0 0 40px; +/* text-align: justify; */ + } + +.widecolumn .post { + margin: 0; + } + +.narrowcolumn .postmetadata { + padding-top: 5px; + } + +.widecolumn .postmetadata { + margin: 30px 0; + } + +.widecolumn .smallattachment { + text-align: center; + float: left; + width: 128px; + margin: 5px 5px 5px 0px; +} + +.widecolumn .attachment { + text-align: center; + margin: 5px 0px; +} + +.postmetadata { + clear: left; +} + +#footer { + padding: 0; + margin: 0 auto; + width: 760px; + clear: both; + } + +#footer p { + margin: 0; + padding: 20px 0; + text-align: center; + } +/* End Structure */ + + + +/* Begin Headers */ +h1 { + padding-top: 30px; + margin: 0; + } + +h2 { + margin: 30px 0 0; + } + +h2.pagetitle { + margin-top: 30px; + text-align: center; +} + +#sidebar h2 { + margin: 5px 0 0; + padding: 0; + } + +h3 { + padding: 0; + margin: 30px 0 0; + } + +h3.comments { + padding: 0; + margin: 40px auto 20px ; + } +/* End Headers */ + + + +/* Begin Images */ +p img { + padding: 0; + max-width: 100%; + } + +/* Using 'class="alignright"' on an image will (who would've + thought?!) align the image to the right. And using 'class="centered', + will of course center the image. This is much better than using + align="center", being much more futureproof (and valid) */ + +img.centered { + display: block; + margin-left: auto; + margin-right: auto; + } + +img.alignright { + padding: 4px; + margin: 0 0 2px 7px; + display: inline; + } + +img.alignleft { + padding: 4px; + margin: 0 7px 2px 0; + display: inline; + } + +.alignright { + float: right; + } + +.alignleft { + float: left + } +/* End Images */ + + + +/* Begin Lists + + Special stylized non-IE bullets + Do not work in Internet Explorer, which merely default to normal bullets. */ + +html>body .entry ul { + margin-left: 0px; + padding: 0 0 0 30px; + list-style: none; + padding-left: 10px; + text-indent: -10px; + } + +html>body .entry li { + margin: 7px 0 8px 10px; + } + +.entry ul li:before, #sidebar ul ul li:before { + content: "\00BB \0020"; + } + +.entry ol { + padding: 0 0 0 35px; + margin: 0; + } + +.entry ol li { + margin: 0; + padding: 0; + } + +.postmetadata ul, .postmetadata li { + display: inline; + list-style-type: none; + list-style-image: none; + } + +#sidebar ul, #sidebar ul ol { + margin: 0; + padding: 0; + } + +#sidebar ul li { + list-style-type: none; + list-style-image: none; + margin-bottom: 15px; + } + +#sidebar ul p, #sidebar ul select { + margin: 5px 0 8px; + } + +#sidebar ul ul, #sidebar ul ol { + margin: 5px 0 0 10px; + } + +#sidebar ul ul ul, #sidebar ul ol { + margin: 0 0 0 10px; + } + +ol li, #sidebar ul ol li { + list-style: decimal outside; + } + +#sidebar ul ul li, #sidebar ul ol li { + margin: 3px 0 0; + padding: 0; + } +/* End Entry Lists */ + + + +/* Begin Form Elements */ +#searchform { + margin: 10px auto; + padding: 5px 3px; + text-align: center; + } + +#sidebar #searchform #s { + width: 108px; + padding: 2px; + } + +#sidebar #searchsubmit { + padding: 1px; + } + +.entry form { /* This is mainly for password protected posts, makes them look better. */ + text-align:center; + } + +select { + width: 130px; + } + +#commentform input { + width: 170px; + padding: 2px; + margin: 5px 5px 1px 0; + } + +#commentform textarea { + width: 100%; + padding: 2px; + } + +#commentform #submit { + margin: 0; + float: right; + } +/* End Form Elements */ + + + +/* Begin Comments*/ +.alt { + margin: 0; + padding: 10px; + } + +.commentlist { + padding: 0; +/* text-align: justify; */ + } + +.commentlist li { + margin: 15px 0 3px; + padding: 5px 10px 3px; + list-style: none; + } + +.commentlist p { + margin: 10px 5px 10px 0; + } + +#commentform p { + margin: 5px 0; + } + +.nocomments { + text-align: center; + margin: 0; + padding: 0; + } + +.commentmetadata { + margin: 0; + display: block; + } +/* End Comments */ + + + +/* Begin Sidebar */ +#sidebar +{ + padding: 20px 0 10px 0; + margin-left: 545px; + width: 190px; + } + +#sidebar form { + margin: 0; + } +/* End Sidebar */ + + + +/* Begin Calendar */ +#wp-calendar { + empty-cells: show; + margin: 10px auto 0; + width: 155px; + } + +#wp-calendar #next a { + padding-right: 10px; + text-align: right; + } + +#wp-calendar #prev a { + padding-left: 10px; + text-align: left; + } + +#wp-calendar a { + display: block; + } + +#wp-calendar caption { + text-align: center; + width: 100%; + } + +#wp-calendar td { + padding: 3px 0; + text-align: center; + } + +#wp-calendar td.pad:hover { /* Doesn't work in IE */ + background-color: #fff; } +/* End Calendar */ + + + +/* Begin Various Tags & Classes */ +acronym, abbr, span.caps { + cursor: help; + } + +acronym, abbr { + border-bottom: 1px dashed #999; + } + +blockquote { + margin: 15px 30px 0 10px; + padding-left: 20px; + border-left: 5px solid #ddd; + } + +blockquote cite { + margin: 5px 0 0; + display: block; + } + +.center { + text-align: center; + } + +a img { + border: none; + } + +.navigation { + display: block; + text-align: center; + margin-top: 10px; + margin-bottom: 60px; + } +/* End Various Tags & Classes*/ + diff --git a/static/mxgraph/examples/editors/diagrameditor.html b/static/mxgraph/examples/editors/diagrameditor.html new file mode 100644 index 0000000..6fa20b3 --- /dev/null +++ b/static/mxgraph/examples/editors/diagrameditor.html @@ -0,0 +1,347 @@ + + + mxDraw Example + + + + + + + + + +
+ +
+
+
+
+ + + + + +
+ + +
+ +
+ +
+
+ +
+ + Source + +
+
+ +
+ + diff --git a/static/mxgraph/examples/editors/diagrams/empty.xml b/static/mxgraph/examples/editors/diagrams/empty.xml new file mode 100644 index 0000000..4d02c06 --- /dev/null +++ b/static/mxgraph/examples/editors/diagrams/empty.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/mxgraph/examples/editors/diagrams/swimlanes.xml b/static/mxgraph/examples/editors/diagrams/swimlanes.xml new file mode 100644 index 0000000..0b38f4d --- /dev/null +++ b/static/mxgraph/examples/editors/diagrams/swimlanes.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/diagrams/travel-booking.xml b/static/mxgraph/examples/editors/diagrams/travel-booking.xml new file mode 100644 index 0000000..b408aca --- /dev/null +++ b/static/mxgraph/examples/editors/diagrams/travel-booking.xml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/mxgraph/examples/editors/diagrams/withdrawal.xml b/static/mxgraph/examples/editors/diagrams/withdrawal.xml new file mode 100644 index 0000000..42b0bbc --- /dev/null +++ b/static/mxgraph/examples/editors/diagrams/withdrawal.xml @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/editors/grapheditor.html b/static/mxgraph/examples/editors/grapheditor.html new file mode 100644 index 0000000..551ae49 --- /dev/null +++ b/static/mxgraph/examples/editors/grapheditor.html @@ -0,0 +1,9 @@ + + + Deprecation Warning + + +This example has been deprecated. A new implementation is available
here. +A copy of the old example is here. + + diff --git a/static/mxgraph/examples/editors/help/index-all.html b/static/mxgraph/examples/editors/help/index-all.html new file mode 100644 index 0000000..3f03c1d --- /dev/null +++ b/static/mxgraph/examples/editors/help/index-all.html @@ -0,0 +1,45 @@ + + + mxGraph Workflow Editor Help Index + + + +

Help Index

+

Help Index

+
+ Lorem Ipsum
+ Lorem Ipsum
+ Lorem Ipsum
+
+ Lorem Ipsum
+ Lorem Ipsum
+ Lorem Ipsum
+ Lorem Ipsum
+
+ Lorem Ipsum
+ Lorem Ipsum
+ Lorem Ipsum
+ Lorem Ipsum
+ + diff --git a/static/mxgraph/examples/editors/help/index.html b/static/mxgraph/examples/editors/help/index.html new file mode 100644 index 0000000..cb0e720 --- /dev/null +++ b/static/mxgraph/examples/editors/help/index.html @@ -0,0 +1,41 @@ + + + mxGraph Workflow Editor Help + + + +

mxGraph Online Help

+

+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et +dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip +ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt +mollit anim id est laborum. +

+ See Help Index + + diff --git a/static/mxgraph/examples/editors/images/actor.gif b/static/mxgraph/examples/editors/images/actor.gif new file mode 100644 index 0000000..1f76527 Binary files /dev/null and b/static/mxgraph/examples/editors/images/actor.gif differ diff --git a/static/mxgraph/examples/editors/images/alignbottom.gif b/static/mxgraph/examples/editors/images/alignbottom.gif new file mode 100644 index 0000000..bdbfc28 Binary files /dev/null and b/static/mxgraph/examples/editors/images/alignbottom.gif differ diff --git a/static/mxgraph/examples/editors/images/aligncenter.gif b/static/mxgraph/examples/editors/images/aligncenter.gif new file mode 100644 index 0000000..70458d4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/aligncenter.gif differ diff --git a/static/mxgraph/examples/editors/images/alignleft.gif b/static/mxgraph/examples/editors/images/alignleft.gif new file mode 100644 index 0000000..2702815 Binary files /dev/null and b/static/mxgraph/examples/editors/images/alignleft.gif differ diff --git a/static/mxgraph/examples/editors/images/alignmiddle.gif b/static/mxgraph/examples/editors/images/alignmiddle.gif new file mode 100644 index 0000000..bdd4013 Binary files /dev/null and b/static/mxgraph/examples/editors/images/alignmiddle.gif differ diff --git a/static/mxgraph/examples/editors/images/alignright.gif b/static/mxgraph/examples/editors/images/alignright.gif new file mode 100644 index 0000000..3530a61 Binary files /dev/null and b/static/mxgraph/examples/editors/images/alignright.gif differ diff --git a/static/mxgraph/examples/editors/images/aligntop.gif b/static/mxgraph/examples/editors/images/aligntop.gif new file mode 100644 index 0000000..7b06da4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/aligntop.gif differ diff --git a/static/mxgraph/examples/editors/images/arrow.gif b/static/mxgraph/examples/editors/images/arrow.gif new file mode 100644 index 0000000..28b08e5 Binary files /dev/null and b/static/mxgraph/examples/editors/images/arrow.gif differ diff --git a/static/mxgraph/examples/editors/images/bell.png b/static/mxgraph/examples/editors/images/bell.png new file mode 100644 index 0000000..3d30cc9 Binary files /dev/null and b/static/mxgraph/examples/editors/images/bell.png differ diff --git a/static/mxgraph/examples/editors/images/bg.gif b/static/mxgraph/examples/editors/images/bg.gif new file mode 100644 index 0000000..9a0cc74 Binary files /dev/null and b/static/mxgraph/examples/editors/images/bg.gif differ diff --git a/static/mxgraph/examples/editors/images/block_end.gif b/static/mxgraph/examples/editors/images/block_end.gif new file mode 100644 index 0000000..1079eae Binary files /dev/null and b/static/mxgraph/examples/editors/images/block_end.gif differ diff --git a/static/mxgraph/examples/editors/images/block_start.gif b/static/mxgraph/examples/editors/images/block_start.gif new file mode 100644 index 0000000..deacc19 Binary files /dev/null and b/static/mxgraph/examples/editors/images/block_start.gif differ diff --git a/static/mxgraph/examples/editors/images/bold.gif b/static/mxgraph/examples/editors/images/bold.gif new file mode 100644 index 0000000..06d523c Binary files /dev/null and b/static/mxgraph/examples/editors/images/bold.gif differ diff --git a/static/mxgraph/examples/editors/images/bottom.gif b/static/mxgraph/examples/editors/images/bottom.gif new file mode 100644 index 0000000..24fa944 Binary files /dev/null and b/static/mxgraph/examples/editors/images/bottom.gif differ diff --git a/static/mxgraph/examples/editors/images/box.png b/static/mxgraph/examples/editors/images/box.png new file mode 100644 index 0000000..cbff40d Binary files /dev/null and b/static/mxgraph/examples/editors/images/box.png differ diff --git a/static/mxgraph/examples/editors/images/camera.gif b/static/mxgraph/examples/editors/images/camera.gif new file mode 100644 index 0000000..2d154fa Binary files /dev/null and b/static/mxgraph/examples/editors/images/camera.gif differ diff --git a/static/mxgraph/examples/editors/images/center.gif b/static/mxgraph/examples/editors/images/center.gif new file mode 100644 index 0000000..34c09f3 Binary files /dev/null and b/static/mxgraph/examples/editors/images/center.gif differ diff --git a/static/mxgraph/examples/editors/images/classic_end.gif b/static/mxgraph/examples/editors/images/classic_end.gif new file mode 100644 index 0000000..69693e3 Binary files /dev/null and b/static/mxgraph/examples/editors/images/classic_end.gif differ diff --git a/static/mxgraph/examples/editors/images/classic_start.gif b/static/mxgraph/examples/editors/images/classic_start.gif new file mode 100644 index 0000000..9d25f09 Binary files /dev/null and b/static/mxgraph/examples/editors/images/classic_start.gif differ diff --git a/static/mxgraph/examples/editors/images/cloud.gif b/static/mxgraph/examples/editors/images/cloud.gif new file mode 100644 index 0000000..560612e Binary files /dev/null and b/static/mxgraph/examples/editors/images/cloud.gif differ diff --git a/static/mxgraph/examples/editors/images/cmp-bg.gif b/static/mxgraph/examples/editors/images/cmp-bg.gif new file mode 100644 index 0000000..5eb8b88 Binary files /dev/null and b/static/mxgraph/examples/editors/images/cmp-bg.gif differ diff --git a/static/mxgraph/examples/editors/images/collapse.gif b/static/mxgraph/examples/editors/images/collapse.gif new file mode 100644 index 0000000..c45625e Binary files /dev/null and b/static/mxgraph/examples/editors/images/collapse.gif differ diff --git a/static/mxgraph/examples/editors/images/connect.gif b/static/mxgraph/examples/editors/images/connect.gif new file mode 100644 index 0000000..39f5d21 Binary files /dev/null and b/static/mxgraph/examples/editors/images/connect.gif differ diff --git a/static/mxgraph/examples/editors/images/connector.gif b/static/mxgraph/examples/editors/images/connector.gif new file mode 100644 index 0000000..326e061 Binary files /dev/null and b/static/mxgraph/examples/editors/images/connector.gif differ diff --git a/static/mxgraph/examples/editors/images/console.gif b/static/mxgraph/examples/editors/images/console.gif new file mode 100644 index 0000000..a598f60 Binary files /dev/null and b/static/mxgraph/examples/editors/images/console.gif differ diff --git a/static/mxgraph/examples/editors/images/copy.gif b/static/mxgraph/examples/editors/images/copy.gif new file mode 100644 index 0000000..18c903e Binary files /dev/null and b/static/mxgraph/examples/editors/images/copy.gif differ diff --git a/static/mxgraph/examples/editors/images/cube_green.png b/static/mxgraph/examples/editors/images/cube_green.png new file mode 100644 index 0000000..af0cdc1 Binary files /dev/null and b/static/mxgraph/examples/editors/images/cube_green.png differ diff --git a/static/mxgraph/examples/editors/images/cut.gif b/static/mxgraph/examples/editors/images/cut.gif new file mode 100644 index 0000000..d5ffbbd Binary files /dev/null and b/static/mxgraph/examples/editors/images/cut.gif differ diff --git a/static/mxgraph/examples/editors/images/cylinder.gif b/static/mxgraph/examples/editors/images/cylinder.gif new file mode 100644 index 0000000..89c03e6 Binary files /dev/null and b/static/mxgraph/examples/editors/images/cylinder.gif differ diff --git a/static/mxgraph/examples/editors/images/delete.gif b/static/mxgraph/examples/editors/images/delete.gif new file mode 100644 index 0000000..e1956be Binary files /dev/null and b/static/mxgraph/examples/editors/images/delete.gif differ diff --git a/static/mxgraph/examples/editors/images/diagram.gif b/static/mxgraph/examples/editors/images/diagram.gif new file mode 100644 index 0000000..66a5465 Binary files /dev/null and b/static/mxgraph/examples/editors/images/diagram.gif differ diff --git a/static/mxgraph/examples/editors/images/diamond_end.gif b/static/mxgraph/examples/editors/images/diamond_end.gif new file mode 100644 index 0000000..7b42fb5 Binary files /dev/null and b/static/mxgraph/examples/editors/images/diamond_end.gif differ diff --git a/static/mxgraph/examples/editors/images/diamond_start.gif b/static/mxgraph/examples/editors/images/diamond_start.gif new file mode 100644 index 0000000..8d5ba05 Binary files /dev/null and b/static/mxgraph/examples/editors/images/diamond_start.gif differ diff --git a/static/mxgraph/examples/editors/images/doubleellipse.gif b/static/mxgraph/examples/editors/images/doubleellipse.gif new file mode 100644 index 0000000..9f1380f Binary files /dev/null and b/static/mxgraph/examples/editors/images/doubleellipse.gif differ diff --git a/static/mxgraph/examples/editors/images/down.gif b/static/mxgraph/examples/editors/images/down.gif new file mode 100644 index 0000000..3bed86e Binary files /dev/null and b/static/mxgraph/examples/editors/images/down.gif differ diff --git a/static/mxgraph/examples/editors/images/draw/drawbg.jpg b/static/mxgraph/examples/editors/images/draw/drawbg.jpg new file mode 100644 index 0000000..c538818 Binary files /dev/null and b/static/mxgraph/examples/editors/images/draw/drawbg.jpg differ diff --git a/static/mxgraph/examples/editors/images/draw/drawbgcolor.jpg b/static/mxgraph/examples/editors/images/draw/drawbgcolor.jpg new file mode 100644 index 0000000..4653b68 Binary files /dev/null and b/static/mxgraph/examples/editors/images/draw/drawbgcolor.jpg differ diff --git a/static/mxgraph/examples/editors/images/draw/drawfooter.jpg b/static/mxgraph/examples/editors/images/draw/drawfooter.jpg new file mode 100644 index 0000000..d7a4c65 Binary files /dev/null and b/static/mxgraph/examples/editors/images/draw/drawfooter.jpg differ diff --git a/static/mxgraph/examples/editors/images/draw/drawheader.jpg b/static/mxgraph/examples/editors/images/draw/drawheader.jpg new file mode 100644 index 0000000..e1f8c9d Binary files /dev/null and b/static/mxgraph/examples/editors/images/draw/drawheader.jpg differ diff --git a/static/mxgraph/examples/editors/images/draw/mxlogo.jpg b/static/mxgraph/examples/editors/images/draw/mxlogo.jpg new file mode 100644 index 0000000..f9b9fa4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/draw/mxlogo.jpg differ diff --git a/static/mxgraph/examples/editors/images/dude3.png b/static/mxgraph/examples/editors/images/dude3.png new file mode 100644 index 0000000..fa5ca5a Binary files /dev/null and b/static/mxgraph/examples/editors/images/dude3.png differ diff --git a/static/mxgraph/examples/editors/images/earth.png b/static/mxgraph/examples/editors/images/earth.png new file mode 100644 index 0000000..4493880 Binary files /dev/null and b/static/mxgraph/examples/editors/images/earth.png differ diff --git a/static/mxgraph/examples/editors/images/ellipse.gif b/static/mxgraph/examples/editors/images/ellipse.gif new file mode 100644 index 0000000..5b5fad0 Binary files /dev/null and b/static/mxgraph/examples/editors/images/ellipse.gif differ diff --git a/static/mxgraph/examples/editors/images/entity.gif b/static/mxgraph/examples/editors/images/entity.gif new file mode 100644 index 0000000..d04e93a Binary files /dev/null and b/static/mxgraph/examples/editors/images/entity.gif differ diff --git a/static/mxgraph/examples/editors/images/expand.gif b/static/mxgraph/examples/editors/images/expand.gif new file mode 100644 index 0000000..7da3ff8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/expand.gif differ diff --git a/static/mxgraph/examples/editors/images/fillcolor.gif b/static/mxgraph/examples/editors/images/fillcolor.gif new file mode 100644 index 0000000..dffe6f5 Binary files /dev/null and b/static/mxgraph/examples/editors/images/fillcolor.gif differ diff --git a/static/mxgraph/examples/editors/images/fit.gif b/static/mxgraph/examples/editors/images/fit.gif new file mode 100644 index 0000000..8d7bff8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/fit.gif differ diff --git a/static/mxgraph/examples/editors/images/font.gif b/static/mxgraph/examples/editors/images/font.gif new file mode 100644 index 0000000..7f276c3 Binary files /dev/null and b/static/mxgraph/examples/editors/images/font.gif differ diff --git a/static/mxgraph/examples/editors/images/fontcolor.gif b/static/mxgraph/examples/editors/images/fontcolor.gif new file mode 100644 index 0000000..0a7f017 Binary files /dev/null and b/static/mxgraph/examples/editors/images/fontcolor.gif differ diff --git a/static/mxgraph/examples/editors/images/gear.gif b/static/mxgraph/examples/editors/images/gear.gif new file mode 100644 index 0000000..30310d6 Binary files /dev/null and b/static/mxgraph/examples/editors/images/gear.gif differ diff --git a/static/mxgraph/examples/editors/images/gear.png b/static/mxgraph/examples/editors/images/gear.png new file mode 100644 index 0000000..647d897 Binary files /dev/null and b/static/mxgraph/examples/editors/images/gear.png differ diff --git a/static/mxgraph/examples/editors/images/grid.gif b/static/mxgraph/examples/editors/images/grid.gif new file mode 100644 index 0000000..a82a20d Binary files /dev/null and b/static/mxgraph/examples/editors/images/grid.gif differ diff --git a/static/mxgraph/examples/editors/images/group.gif b/static/mxgraph/examples/editors/images/group.gif new file mode 100644 index 0000000..af79836 Binary files /dev/null and b/static/mxgraph/examples/editors/images/group.gif differ diff --git a/static/mxgraph/examples/editors/images/help.gif b/static/mxgraph/examples/editors/images/help.gif new file mode 100644 index 0000000..35d7a1e Binary files /dev/null and b/static/mxgraph/examples/editors/images/help.gif differ diff --git a/static/mxgraph/examples/editors/images/hexagon.gif b/static/mxgraph/examples/editors/images/hexagon.gif new file mode 100644 index 0000000..6c0568a Binary files /dev/null and b/static/mxgraph/examples/editors/images/hexagon.gif differ diff --git a/static/mxgraph/examples/editors/images/hline.gif b/static/mxgraph/examples/editors/images/hline.gif new file mode 100644 index 0000000..803f8a3 Binary files /dev/null and b/static/mxgraph/examples/editors/images/hline.gif differ diff --git a/static/mxgraph/examples/editors/images/house.gif b/static/mxgraph/examples/editors/images/house.gif new file mode 100644 index 0000000..84c6b15 Binary files /dev/null and b/static/mxgraph/examples/editors/images/house.gif differ diff --git a/static/mxgraph/examples/editors/images/house.png b/static/mxgraph/examples/editors/images/house.png new file mode 100644 index 0000000..e4986bc Binary files /dev/null and b/static/mxgraph/examples/editors/images/house.png differ diff --git a/static/mxgraph/examples/editors/images/image.gif b/static/mxgraph/examples/editors/images/image.gif new file mode 100644 index 0000000..0a50356 Binary files /dev/null and b/static/mxgraph/examples/editors/images/image.gif differ diff --git a/static/mxgraph/examples/editors/images/italic.gif b/static/mxgraph/examples/editors/images/italic.gif new file mode 100644 index 0000000..301cfc7 Binary files /dev/null and b/static/mxgraph/examples/editors/images/italic.gif differ diff --git a/static/mxgraph/examples/editors/images/left.gif b/static/mxgraph/examples/editors/images/left.gif new file mode 100644 index 0000000..235e780 Binary files /dev/null and b/static/mxgraph/examples/editors/images/left.gif differ diff --git a/static/mxgraph/examples/editors/images/linecolor.gif b/static/mxgraph/examples/editors/images/linecolor.gif new file mode 100644 index 0000000..96068a6 Binary files /dev/null and b/static/mxgraph/examples/editors/images/linecolor.gif differ diff --git a/static/mxgraph/examples/editors/images/link.gif b/static/mxgraph/examples/editors/images/link.gif new file mode 100644 index 0000000..8681802 Binary files /dev/null and b/static/mxgraph/examples/editors/images/link.gif differ diff --git a/static/mxgraph/examples/editors/images/loading.gif b/static/mxgraph/examples/editors/images/loading.gif new file mode 100644 index 0000000..7bb834d Binary files /dev/null and b/static/mxgraph/examples/editors/images/loading.gif differ diff --git a/static/mxgraph/examples/editors/images/middle.gif b/static/mxgraph/examples/editors/images/middle.gif new file mode 100644 index 0000000..33eed00 Binary files /dev/null and b/static/mxgraph/examples/editors/images/middle.gif differ diff --git a/static/mxgraph/examples/editors/images/new.gif b/static/mxgraph/examples/editors/images/new.gif new file mode 100644 index 0000000..6fcd1bc Binary files /dev/null and b/static/mxgraph/examples/editors/images/new.gif differ diff --git a/static/mxgraph/examples/editors/images/open.gif b/static/mxgraph/examples/editors/images/open.gif new file mode 100644 index 0000000..2df1d89 Binary files /dev/null and b/static/mxgraph/examples/editors/images/open.gif differ diff --git a/static/mxgraph/examples/editors/images/open_end.gif b/static/mxgraph/examples/editors/images/open_end.gif new file mode 100644 index 0000000..5ae275e Binary files /dev/null and b/static/mxgraph/examples/editors/images/open_end.gif differ diff --git a/static/mxgraph/examples/editors/images/open_start.gif b/static/mxgraph/examples/editors/images/open_start.gif new file mode 100644 index 0000000..d58ca46 Binary files /dev/null and b/static/mxgraph/examples/editors/images/open_start.gif differ diff --git a/static/mxgraph/examples/editors/images/outline.gif b/static/mxgraph/examples/editors/images/outline.gif new file mode 100644 index 0000000..e04d92b Binary files /dev/null and b/static/mxgraph/examples/editors/images/outline.gif differ diff --git a/static/mxgraph/examples/editors/images/oval_end.gif b/static/mxgraph/examples/editors/images/oval_end.gif new file mode 100644 index 0000000..c13fc6a Binary files /dev/null and b/static/mxgraph/examples/editors/images/oval_end.gif differ diff --git a/static/mxgraph/examples/editors/images/oval_start.gif b/static/mxgraph/examples/editors/images/oval_start.gif new file mode 100644 index 0000000..213de96 Binary files /dev/null and b/static/mxgraph/examples/editors/images/oval_start.gif differ diff --git a/static/mxgraph/examples/editors/images/overlays/check.png b/static/mxgraph/examples/editors/images/overlays/check.png new file mode 100644 index 0000000..3f3110a Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/check.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/error.png b/static/mxgraph/examples/editors/images/overlays/error.png new file mode 100644 index 0000000..eb1491b Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/error.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/flash.png b/static/mxgraph/examples/editors/images/overlays/flash.png new file mode 100644 index 0000000..aa6fd2d Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/flash.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/forbidden.png b/static/mxgraph/examples/editors/images/overlays/forbidden.png new file mode 100644 index 0000000..04d5f4a Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/forbidden.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/help.png b/static/mxgraph/examples/editors/images/overlays/help.png new file mode 100644 index 0000000..e8ceddb Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/help.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/house.png b/static/mxgraph/examples/editors/images/overlays/house.png new file mode 100644 index 0000000..c652bc8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/house.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/information.png b/static/mxgraph/examples/editors/images/overlays/information.png new file mode 100644 index 0000000..5df1857 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/information.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/lightbulb_on.png b/static/mxgraph/examples/editors/images/overlays/lightbulb_on.png new file mode 100644 index 0000000..8b59412 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/lightbulb_on.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/pencil.png b/static/mxgraph/examples/editors/images/overlays/pencil.png new file mode 100644 index 0000000..712443e Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/pencil.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/printer.png b/static/mxgraph/examples/editors/images/overlays/printer.png new file mode 100644 index 0000000..6004816 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/printer.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/user3.png b/static/mxgraph/examples/editors/images/overlays/user3.png new file mode 100644 index 0000000..7e83258 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/user3.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/users3.png b/static/mxgraph/examples/editors/images/overlays/users3.png new file mode 100644 index 0000000..2185bf4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/users3.png differ diff --git a/static/mxgraph/examples/editors/images/overlays/workplace.png b/static/mxgraph/examples/editors/images/overlays/workplace.png new file mode 100644 index 0000000..2a0bedd Binary files /dev/null and b/static/mxgraph/examples/editors/images/overlays/workplace.png differ diff --git a/static/mxgraph/examples/editors/images/package.png b/static/mxgraph/examples/editors/images/package.png new file mode 100644 index 0000000..2bc6123 Binary files /dev/null and b/static/mxgraph/examples/editors/images/package.png differ diff --git a/static/mxgraph/examples/editors/images/pan.gif b/static/mxgraph/examples/editors/images/pan.gif new file mode 100644 index 0000000..ecd68bf Binary files /dev/null and b/static/mxgraph/examples/editors/images/pan.gif differ diff --git a/static/mxgraph/examples/editors/images/paste.gif b/static/mxgraph/examples/editors/images/paste.gif new file mode 100644 index 0000000..242ecb1 Binary files /dev/null and b/static/mxgraph/examples/editors/images/paste.gif differ diff --git a/static/mxgraph/examples/editors/images/plain.gif b/static/mxgraph/examples/editors/images/plain.gif new file mode 100644 index 0000000..6c914d8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/plain.gif differ diff --git a/static/mxgraph/examples/editors/images/preferences.gif b/static/mxgraph/examples/editors/images/preferences.gif new file mode 100644 index 0000000..1f6407e Binary files /dev/null and b/static/mxgraph/examples/editors/images/preferences.gif differ diff --git a/static/mxgraph/examples/editors/images/press.gif b/static/mxgraph/examples/editors/images/press.gif new file mode 100644 index 0000000..75b4177 Binary files /dev/null and b/static/mxgraph/examples/editors/images/press.gif differ diff --git a/static/mxgraph/examples/editors/images/preview.gif b/static/mxgraph/examples/editors/images/preview.gif new file mode 100644 index 0000000..a77258b Binary files /dev/null and b/static/mxgraph/examples/editors/images/preview.gif differ diff --git a/static/mxgraph/examples/editors/images/print.gif b/static/mxgraph/examples/editors/images/print.gif new file mode 100644 index 0000000..ac948df Binary files /dev/null and b/static/mxgraph/examples/editors/images/print.gif differ diff --git a/static/mxgraph/examples/editors/images/printer.png b/static/mxgraph/examples/editors/images/printer.png new file mode 100644 index 0000000..7d36468 Binary files /dev/null and b/static/mxgraph/examples/editors/images/printer.png differ diff --git a/static/mxgraph/examples/editors/images/properties.gif b/static/mxgraph/examples/editors/images/properties.gif new file mode 100644 index 0000000..509b3fe Binary files /dev/null and b/static/mxgraph/examples/editors/images/properties.gif differ diff --git a/static/mxgraph/examples/editors/images/rectangle.gif b/static/mxgraph/examples/editors/images/rectangle.gif new file mode 100644 index 0000000..116acc6 Binary files /dev/null and b/static/mxgraph/examples/editors/images/rectangle.gif differ diff --git a/static/mxgraph/examples/editors/images/redo.gif b/static/mxgraph/examples/editors/images/redo.gif new file mode 100644 index 0000000..2090f34 Binary files /dev/null and b/static/mxgraph/examples/editors/images/redo.gif differ diff --git a/static/mxgraph/examples/editors/images/refresh.gif b/static/mxgraph/examples/editors/images/refresh.gif new file mode 100644 index 0000000..2f7163a Binary files /dev/null and b/static/mxgraph/examples/editors/images/refresh.gif differ diff --git a/static/mxgraph/examples/editors/images/rhombus.gif b/static/mxgraph/examples/editors/images/rhombus.gif new file mode 100644 index 0000000..bd042c0 Binary files /dev/null and b/static/mxgraph/examples/editors/images/rhombus.gif differ diff --git a/static/mxgraph/examples/editors/images/right.gif b/static/mxgraph/examples/editors/images/right.gif new file mode 100644 index 0000000..9c06d30 Binary files /dev/null and b/static/mxgraph/examples/editors/images/right.gif differ diff --git a/static/mxgraph/examples/editors/images/rounded.gif b/static/mxgraph/examples/editors/images/rounded.gif new file mode 100644 index 0000000..4159f6d Binary files /dev/null and b/static/mxgraph/examples/editors/images/rounded.gif differ diff --git a/static/mxgraph/examples/editors/images/save.gif b/static/mxgraph/examples/editors/images/save.gif new file mode 100644 index 0000000..d0d261c Binary files /dev/null and b/static/mxgraph/examples/editors/images/save.gif differ diff --git a/static/mxgraph/examples/editors/images/saveas.gif b/static/mxgraph/examples/editors/images/saveas.gif new file mode 100644 index 0000000..4a114af Binary files /dev/null and b/static/mxgraph/examples/editors/images/saveas.gif differ diff --git a/static/mxgraph/examples/editors/images/script.gif b/static/mxgraph/examples/editors/images/script.gif new file mode 100644 index 0000000..9785422 Binary files /dev/null and b/static/mxgraph/examples/editors/images/script.gif differ diff --git a/static/mxgraph/examples/editors/images/select.gif b/static/mxgraph/examples/editors/images/select.gif new file mode 100644 index 0000000..bbe3c08 Binary files /dev/null and b/static/mxgraph/examples/editors/images/select.gif differ diff --git a/static/mxgraph/examples/editors/images/server.png b/static/mxgraph/examples/editors/images/server.png new file mode 100644 index 0000000..9621c6e Binary files /dev/null and b/static/mxgraph/examples/editors/images/server.png differ diff --git a/static/mxgraph/examples/editors/images/straight.gif b/static/mxgraph/examples/editors/images/straight.gif new file mode 100644 index 0000000..3b5b061 Binary files /dev/null and b/static/mxgraph/examples/editors/images/straight.gif differ diff --git a/static/mxgraph/examples/editors/images/swimlane.gif b/static/mxgraph/examples/editors/images/swimlane.gif new file mode 100644 index 0000000..3506687 Binary files /dev/null and b/static/mxgraph/examples/editors/images/swimlane.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/cancel_end.png b/static/mxgraph/examples/editors/images/symbols/cancel_end.png new file mode 100644 index 0000000..7aa854c Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/cancel_end.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/cancel_intermediate.png b/static/mxgraph/examples/editors/images/symbols/cancel_intermediate.png new file mode 100644 index 0000000..920299c Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/cancel_intermediate.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/error.png b/static/mxgraph/examples/editors/images/symbols/error.png new file mode 100644 index 0000000..ffcfe8a Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/error.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/event.png b/static/mxgraph/examples/editors/images/symbols/event.png new file mode 100644 index 0000000..50486be Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/event.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/event_end.png b/static/mxgraph/examples/editors/images/symbols/event_end.png new file mode 100644 index 0000000..928d083 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/event_end.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/event_intermediate.png b/static/mxgraph/examples/editors/images/symbols/event_intermediate.png new file mode 100644 index 0000000..f14bd80 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/event_intermediate.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/fork.png b/static/mxgraph/examples/editors/images/symbols/fork.png new file mode 100644 index 0000000..042cd9b Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/fork.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/inclusive.png b/static/mxgraph/examples/editors/images/symbols/inclusive.png new file mode 100644 index 0000000..6111cc1 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/inclusive.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/link.png b/static/mxgraph/examples/editors/images/symbols/link.png new file mode 100644 index 0000000..792572b Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/link.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/merge.png b/static/mxgraph/examples/editors/images/symbols/merge.png new file mode 100644 index 0000000..abc17eb Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/merge.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/message.png b/static/mxgraph/examples/editors/images/symbols/message.png new file mode 100644 index 0000000..21cc97d Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/message.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/message_end.png b/static/mxgraph/examples/editors/images/symbols/message_end.png new file mode 100644 index 0000000..3b2a4d8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/message_end.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/message_intermediate.png b/static/mxgraph/examples/editors/images/symbols/message_intermediate.png new file mode 100644 index 0000000..80b2504 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/message_intermediate.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/multiple.png b/static/mxgraph/examples/editors/images/symbols/multiple.png new file mode 100644 index 0000000..89a1200 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/multiple.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/rule.png b/static/mxgraph/examples/editors/images/symbols/rule.png new file mode 100644 index 0000000..4b2d769 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/rule.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_cancel_end.gif b/static/mxgraph/examples/editors/images/symbols/small_cancel_end.gif new file mode 100644 index 0000000..42adc37 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_cancel_end.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_cancel_intermediate.gif b/static/mxgraph/examples/editors/images/symbols/small_cancel_intermediate.gif new file mode 100644 index 0000000..8a806f2 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_cancel_intermediate.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_error.gif b/static/mxgraph/examples/editors/images/symbols/small_error.gif new file mode 100644 index 0000000..00fa235 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_error.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_event.gif b/static/mxgraph/examples/editors/images/symbols/small_event.gif new file mode 100644 index 0000000..4ccdad9 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_event.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_event_end.gif b/static/mxgraph/examples/editors/images/symbols/small_event_end.gif new file mode 100644 index 0000000..a0c89b4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_event_end.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_event_intermediate.gif b/static/mxgraph/examples/editors/images/symbols/small_event_intermediate.gif new file mode 100644 index 0000000..14bc10a Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_event_intermediate.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_fork.gif b/static/mxgraph/examples/editors/images/symbols/small_fork.gif new file mode 100644 index 0000000..5ed72e4 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_fork.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_inclusive.gif b/static/mxgraph/examples/editors/images/symbols/small_inclusive.gif new file mode 100644 index 0000000..a10da9d Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_inclusive.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_link.gif b/static/mxgraph/examples/editors/images/symbols/small_link.gif new file mode 100644 index 0000000..0732883 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_link.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_merge.gif b/static/mxgraph/examples/editors/images/symbols/small_merge.gif new file mode 100644 index 0000000..f0c6166 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_merge.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_message.gif b/static/mxgraph/examples/editors/images/symbols/small_message.gif new file mode 100644 index 0000000..1940da8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_message.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_message_end.gif b/static/mxgraph/examples/editors/images/symbols/small_message_end.gif new file mode 100644 index 0000000..467b024 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_message_end.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_message_intermediate.gif b/static/mxgraph/examples/editors/images/symbols/small_message_intermediate.gif new file mode 100644 index 0000000..d6a58f1 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_message_intermediate.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_multiple.gif b/static/mxgraph/examples/editors/images/symbols/small_multiple.gif new file mode 100644 index 0000000..9d2b675 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_multiple.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_rule.gif b/static/mxgraph/examples/editors/images/symbols/small_rule.gif new file mode 100644 index 0000000..8df6be0 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_rule.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_terminate.gif b/static/mxgraph/examples/editors/images/symbols/small_terminate.gif new file mode 100644 index 0000000..da82067 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_terminate.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/small_timer.gif b/static/mxgraph/examples/editors/images/symbols/small_timer.gif new file mode 100644 index 0000000..8d7841d Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/small_timer.gif differ diff --git a/static/mxgraph/examples/editors/images/symbols/terminate.png b/static/mxgraph/examples/editors/images/symbols/terminate.png new file mode 100644 index 0000000..e862039 Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/terminate.png differ diff --git a/static/mxgraph/examples/editors/images/symbols/timer.png b/static/mxgraph/examples/editors/images/symbols/timer.png new file mode 100644 index 0000000..247eb9e Binary files /dev/null and b/static/mxgraph/examples/editors/images/symbols/timer.png differ diff --git a/static/mxgraph/examples/editors/images/tasks.gif b/static/mxgraph/examples/editors/images/tasks.gif new file mode 100644 index 0000000..96a231f Binary files /dev/null and b/static/mxgraph/examples/editors/images/tasks.gif differ diff --git a/static/mxgraph/examples/editors/images/text.gif b/static/mxgraph/examples/editors/images/text.gif new file mode 100644 index 0000000..5535ce6 Binary files /dev/null and b/static/mxgraph/examples/editors/images/text.gif differ diff --git a/static/mxgraph/examples/editors/images/toback.gif b/static/mxgraph/examples/editors/images/toback.gif new file mode 100644 index 0000000..da3e471 Binary files /dev/null and b/static/mxgraph/examples/editors/images/toback.gif differ diff --git a/static/mxgraph/examples/editors/images/tofront.gif b/static/mxgraph/examples/editors/images/tofront.gif new file mode 100644 index 0000000..c3b6cdc Binary files /dev/null and b/static/mxgraph/examples/editors/images/tofront.gif differ diff --git a/static/mxgraph/examples/editors/images/toolbar.gif b/static/mxgraph/examples/editors/images/toolbar.gif new file mode 100644 index 0000000..e487713 Binary files /dev/null and b/static/mxgraph/examples/editors/images/toolbar.gif differ diff --git a/static/mxgraph/examples/editors/images/top.gif b/static/mxgraph/examples/editors/images/top.gif new file mode 100644 index 0000000..5840a52 Binary files /dev/null and b/static/mxgraph/examples/editors/images/top.gif differ diff --git a/static/mxgraph/examples/editors/images/tree.gif b/static/mxgraph/examples/editors/images/tree.gif new file mode 100644 index 0000000..d067858 Binary files /dev/null and b/static/mxgraph/examples/editors/images/tree.gif differ diff --git a/static/mxgraph/examples/editors/images/triangle.gif b/static/mxgraph/examples/editors/images/triangle.gif new file mode 100644 index 0000000..7847acd Binary files /dev/null and b/static/mxgraph/examples/editors/images/triangle.gif differ diff --git a/static/mxgraph/examples/editors/images/underline.gif b/static/mxgraph/examples/editors/images/underline.gif new file mode 100644 index 0000000..a580b42 Binary files /dev/null and b/static/mxgraph/examples/editors/images/underline.gif differ diff --git a/static/mxgraph/examples/editors/images/undo.gif b/static/mxgraph/examples/editors/images/undo.gif new file mode 100644 index 0000000..30e28ce Binary files /dev/null and b/static/mxgraph/examples/editors/images/undo.gif differ diff --git a/static/mxgraph/examples/editors/images/ungroup.gif b/static/mxgraph/examples/editors/images/ungroup.gif new file mode 100644 index 0000000..0b0139a Binary files /dev/null and b/static/mxgraph/examples/editors/images/ungroup.gif differ diff --git a/static/mxgraph/examples/editors/images/up.gif b/static/mxgraph/examples/editors/images/up.gif new file mode 100644 index 0000000..0716475 Binary files /dev/null and b/static/mxgraph/examples/editors/images/up.gif differ diff --git a/static/mxgraph/examples/editors/images/vertical.gif b/static/mxgraph/examples/editors/images/vertical.gif new file mode 100644 index 0000000..be1ba6c Binary files /dev/null and b/static/mxgraph/examples/editors/images/vertical.gif differ diff --git a/static/mxgraph/examples/editors/images/workplace.png b/static/mxgraph/examples/editors/images/workplace.png new file mode 100644 index 0000000..3e2fad8 Binary files /dev/null and b/static/mxgraph/examples/editors/images/workplace.png differ diff --git a/static/mxgraph/examples/editors/images/wrench.png b/static/mxgraph/examples/editors/images/wrench.png new file mode 100644 index 0000000..1be38b5 Binary files /dev/null and b/static/mxgraph/examples/editors/images/wrench.png differ diff --git a/static/mxgraph/examples/editors/images/zoom.gif b/static/mxgraph/examples/editors/images/zoom.gif new file mode 100644 index 0000000..b7a21a9 Binary files /dev/null and b/static/mxgraph/examples/editors/images/zoom.gif differ diff --git a/static/mxgraph/examples/editors/images/zoomactual.gif b/static/mxgraph/examples/editors/images/zoomactual.gif new file mode 100644 index 0000000..18119d5 Binary files /dev/null and b/static/mxgraph/examples/editors/images/zoomactual.gif differ diff --git a/static/mxgraph/examples/editors/images/zoomin.gif b/static/mxgraph/examples/editors/images/zoomin.gif new file mode 100644 index 0000000..fa1de90 Binary files /dev/null and b/static/mxgraph/examples/editors/images/zoomin.gif differ diff --git a/static/mxgraph/examples/editors/images/zoomout.gif b/static/mxgraph/examples/editors/images/zoomout.gif new file mode 100644 index 0000000..21427dd Binary files /dev/null and b/static/mxgraph/examples/editors/images/zoomout.gif differ diff --git a/static/mxgraph/examples/editors/js/app.js b/static/mxgraph/examples/editors/js/app.js new file mode 100644 index 0000000..fdbd0fc --- /dev/null +++ b/static/mxgraph/examples/editors/js/app.js @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2013, JGraph Ltd + * + * Defines the startup sequence of the application. + */ +{ + + /** + * Constructs a new application (returns an mxEditor instance) + */ + function createEditor(config) + { + var editor = null; + + var hideSplash = function() + { + // Fades-out the splash screen + var splash = document.getElementById('splash'); + + if (splash != null) + { + try + { + mxEvent.release(splash); + mxEffects.fadeOut(splash, 100, true); + } + catch (e) + { + splash.parentNode.removeChild(splash); + } + } + }; + + try + { + if (!mxClient.isBrowserSupported()) + { + mxUtils.error('Browser is not supported!', 200, false); + } + else + { + mxObjectCodec.allowEval = true; + var node = mxUtils.load(config).getDocumentElement(); + editor = new mxEditor(node); + mxObjectCodec.allowEval = false; + + // Adds active border for panning inside the container + editor.graph.createPanningManager = function() + { + var pm = new mxPanningManager(this); + pm.border = 30; + + return pm; + }; + + editor.graph.allowAutoPanning = true; + editor.graph.timerAutoScroll = true; + + // Updates the window title after opening new files + var title = document.title; + var funct = function(sender) + { + document.title = title + ' - ' + sender.getTitle(); + }; + + editor.addListener(mxEvent.OPEN, funct); + + // Prints the current root in the window title if the + // current root of the graph changes (drilling). + editor.addListener(mxEvent.ROOT, funct); + funct(editor); + + // Displays version in statusbar + editor.setStatus('mxGraph '+mxClient.VERSION); + + // Shows the application + hideSplash(); + } + } + catch (e) + { + hideSplash(); + + // Shows an error message if the editor cannot start + mxUtils.alert('Cannot start application: ' + e.message); + throw e; // for debugging + } + + return editor; + } + +} diff --git a/static/mxgraph/examples/editors/layouteditor.html b/static/mxgraph/examples/editors/layouteditor.html new file mode 100644 index 0000000..35a1695 --- /dev/null +++ b/static/mxgraph/examples/editors/layouteditor.html @@ -0,0 +1,34 @@ + + + mxGraph Workflow Designer + + + + + + + +
+ +
+
+ Done +
+ + diff --git a/static/mxgraph/examples/editors/processeditor.html b/static/mxgraph/examples/editors/processeditor.html new file mode 100644 index 0000000..6393d28 --- /dev/null +++ b/static/mxgraph/examples/editors/processeditor.html @@ -0,0 +1,63 @@ + + + mxGraph Process Example + + + + + + + + +
+ +
+
+ +
+
+ Done +
+ + diff --git a/static/mxgraph/examples/editors/resources/app.txt b/static/mxgraph/examples/editors/resources/app.txt new file mode 100644 index 0000000..ae9f116 --- /dev/null +++ b/static/mxgraph/examples/editors/resources/app.txt @@ -0,0 +1,74 @@ +urlHelp=help/index.html +lastSaved=Last Saved +currentFile=Current File +toolbar=Tools +save=Save +print=Print +cut=Cut +copy=Copy +paste=Paste +delete=Delete +undo=Undo +redo=Redo +select=Select +connect=Connect +pan=Pan +zoomIn=Zoom In +zoomOut=Zoom Out +zoom=Zoom +fit=Fit +actualSize=Actual Size +selectAll=Select All +clearSelection=Clear Selection +shape=Shape +format=Format +font=Font +group=Group +ungroup=Ungroup +removeFromParent=Orphan +align=Align +left=Left +right=Right +center=Center +top=Top +middle=Middle +bottom=Bottom +collapse=Collapse +expand=Expand +toBack=Send to back +toFront=Bring to front +enterGroup=Enter group +exitGroup=Exit group +layout=Layout +horizontalTree=Horizontal Tree +verticalTree=Vertical Tree +fillColor=Fill Color +strokeColor=Line Color +gradientColor=Gradient Color +bold=Bold +italic=Italic +fontColor=Font Color +fontSize=Font Size +fontFamily=Font Family +examples=Examples +newDiagram=New Diagram +image=Image +opacity=Opacity +selection=Selection +editStyle=Edit Style +enterStyle=Enter Style +enterColorname=Enter Colorname +enterImageUrl=Enter Image URL +enterOpacity=Enter Opacity (%) +enterFontsize=Enter Fontsize (pt) +enterFontfamily=Enter Fontfamily +toggleRounded=Rounded +toggleShadow=Shadow +openHref=Open URL +show=Show +exportImage=Export Image +exportSvg=Export SVG +changesLost=All changes will be lost! +swimlanes=Swimlanes +travelBooking=Travel Booking +notAvailable=Not available diff --git a/static/mxgraph/examples/editors/resources/app_de.txt b/static/mxgraph/examples/editors/resources/app_de.txt new file mode 100644 index 0000000..21d792c --- /dev/null +++ b/static/mxgraph/examples/editors/resources/app_de.txt @@ -0,0 +1,72 @@ +urlHelp=help/index.html +lastSaved=Zuletzt Gespeichert +currentFile=Aktuelle Datei +toolbar=Tools +save=Speichern +print=Drucken +cut=Ausschneiden +copy=Kopieren +paste=Einfügen +delete=Löschen +undo=Rückgängig +redo=Wiederherstellen +select=Markieren +connect=Verbinden +pan=Verschieben +zoomIn=Hineinzoomen +zoomOut=Herauszoomen +zoom=Zoom +fit=Anpassen +actualSize=Aktuelle Grösse +selectAll=Alle Markieren +clearSelection=Markierung aufheben +shape=Element +format=Format +font=Schrift +group=Gruppieren +ungroup=Gruppe aufheben +removeFromParent=Herauslösen +align=Ausrichten +left=Links +right=Rechts +center=Zentriert +top=Oben +middle=Mitte +bottom=Unten +collapse=Zusammenziehen +expand=Expandieren +toBack=Nach hinten +toFront=Nach vorne +enterGroup=In Gruppe hinein +exitGroup=Aus Gruppe heraus +layout=Anordnen +horizontalTree=Horizontaler Baum +verticalTree=Vertikaler Baum +fillColor=Füllfarbe +strokeColor=Linienfarbe +gradientColor=Farbverlauf +bold=Fett +italic=Kursiv +fontColor=Schriftfarbe +fontSize=Schriftgrösse +fontFamily=Schriftart +examples=Beispiele +newDiagram=Neues Diagramm +image=Bild +opacity=Transparenz +selection=Markierung +editStyle=Style bearbeiten +enterStyle=Style eingeben +enterColorname=Farbname eingeben +enterImageUrl=Bild URL eingeben +enterOpacity=Deckkraft eingeben (%) +enterFontsize=Schriftgrösse eingeben (pt) +enterFontfamily=Schriftart eingeben +toggleRounded=Abgerundet +toggleShadow=Schatten +openHref=URL öffnen +show=Anzeigen +exportImage=Exportiere Bild +exportSvg=Exportiere SVG +changesLost=Alle Aenderungen gehen verloren! +notAvailable=Nicht verfügbar diff --git a/static/mxgraph/examples/editors/resources/app_zh.txt b/static/mxgraph/examples/editors/resources/app_zh.txt new file mode 100644 index 0000000..66b920b --- /dev/null +++ b/static/mxgraph/examples/editors/resources/app_zh.txt @@ -0,0 +1,75 @@ +// Use http://centricle.com/tools/ascii-hex/ for hex codes +urlHelp=help/index.html +lastSaved=Last Saved +currentFile=Current File +toolbar=Tools +save=%u4FDD%u5B58 +print=%u6253%u5370 +cut=%u526a%u5207 +copy=%u590d%u5236 +paste=%u7c98%u8d34 +delete=%u5220%u9664 +undo=%u64a4%u6d88 +redo=%u91cd%u505a +select=Select +connect=Connect +pan=Pan +zoomIn=Zoom In +zoomOut=Zoom Out +zoom=%u5927%u5c0f +fit=%u9002%u5408%u5927%u5c0f +actualSize=Actual Size +selectAll=Select All +clearSelection=Clear Selection +shape=Shape +format=%u683c%u5f0f +font=Font +group=%u5206%u7ec4 +ungroup=%u53d6%u6d88%u5206%u7ec4 +removeFromParent=Orphan +align=%u5bf9%u9f50 +left=Left +right=Right +center=Center +top=Top +middle=Middle +bottom=Bottom +collapse=Collapse +expand=Expand +toBack=Send to back +toFront=Bring to front +enterGroup=Enter group +exitGroup=Exit group +layout=Layout +horizontalTree=Horizontal Tree +verticalTree=Vertical Tree +fillColor=%u586b%u5145%u989c%u8272 +strokeColor=%u7ebf%u989c%u8272 +gradientColor=Gradient Color +bold=%u7c97%u4f53 +italic=%u659c%u4f53 +fontColor=%u5b57%u4f53%u989c%u8272 +fontSize=Font Size +fontFamily=Font Family +examples=Examples +newDiagram=New Diagram +image=Image +opacity=Opacity +selection=Selection +editStyle=Edit Style +enterStyle=Enter Style +enterColorname=Enter Colorname +enterImageUrl=Enter Image URL +enterOpacity=Enter Opacity (%) +enterFontsize=Enter Fontsize (pt) +enterFontfamily=Enter Fontfamily +toggleRounded=Rounded +toggleShadow=Shadow +openHref=Open URL +show=%u663e%u793a +exportImage=Export Image +exportSvg=Export SVG +changesLost=All changes will be lost! +swimlanes=Swimlanes +travelBooking=Travel Booking +notAvailable=Not available diff --git a/static/mxgraph/examples/editors/workfloweditor.html b/static/mxgraph/examples/editors/workfloweditor.html new file mode 100644 index 0000000..9185ad6 --- /dev/null +++ b/static/mxgraph/examples/editors/workfloweditor.html @@ -0,0 +1,65 @@ + + + mxGraph Workflow Example + + + + + + + + + + + + +
+ +
+
+ +
+
+ +
+ + diff --git a/static/mxgraph/examples/events.html b/static/mxgraph/examples/events.html new file mode 100644 index 0000000..19fb476 --- /dev/null +++ b/static/mxgraph/examples/events.html @@ -0,0 +1,167 @@ + + + + Events example for mxGraph + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/extendcanvas.html b/static/mxgraph/examples/extendcanvas.html new file mode 100644 index 0000000..407f8ce --- /dev/null +++ b/static/mxgraph/examples/extendcanvas.html @@ -0,0 +1,238 @@ + + + + Extend canvas example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/fileio.html b/static/mxgraph/examples/fileio.html new file mode 100644 index 0000000..0c71a39 --- /dev/null +++ b/static/mxgraph/examples/fileio.html @@ -0,0 +1,218 @@ + + + + File I/O example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/fileio.txt b/static/mxgraph/examples/fileio.txt new file mode 100644 index 0000000..012410a --- /dev/null +++ b/static/mxgraph/examples/fileio.txt @@ -0,0 +1,19 @@ +# Custom file format for fileio.html (comments start with #, all vertices first) + +# Vertices (id: label) +1:
Last, First
Status
Info +2:
Errorcode
Status
Info +3:
Warning
Status
Info +4:
Groupname
Status
Info +5:
Workplace
Status
Info +6:
Information
Status
Info +7:
Printername
Status
Info + +# Edges (source-id,target-id: label) +1,2: Hint +1,3: News +1,4: Member +5,6: Details +5,7: Access +4,5: Access +1,5: 2-Way diff --git a/static/mxgraph/examples/fileio.xml b/static/mxgraph/examples/fileio.xml new file mode 100644 index 0000000..72d9c34 --- /dev/null +++ b/static/mxgraph/examples/fileio.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/fixedicon.html b/static/mxgraph/examples/fixedicon.html new file mode 100644 index 0000000..ecb84c1 --- /dev/null +++ b/static/mxgraph/examples/fixedicon.html @@ -0,0 +1,90 @@ + + + + Fixed icon example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/fixedpoints.html b/static/mxgraph/examples/fixedpoints.html new file mode 100644 index 0000000..2229190 --- /dev/null +++ b/static/mxgraph/examples/fixedpoints.html @@ -0,0 +1,226 @@ + + + + Fixed points example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/folding.html b/static/mxgraph/examples/folding.html new file mode 100644 index 0000000..e448293 --- /dev/null +++ b/static/mxgraph/examples/folding.html @@ -0,0 +1,167 @@ + + + + Folding example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/grapheditor/README b/static/mxgraph/examples/grapheditor/README new file mode 100644 index 0000000..5c48908 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/README @@ -0,0 +1,5 @@ +Run com.mxgraph.examples.web.GraphEditor in javascript/examples/grapheditor/java/src +(in Eclipse) or execute "ant grapheditor" in the java directory (using Ant) +and point your browser to: + +http://localhost:8080/javascript/examples/grapheditor/www/index.html diff --git a/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/GraphEditor.java b/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/GraphEditor.java new file mode 100644 index 0000000..8192284 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/GraphEditor.java @@ -0,0 +1,58 @@ +package com.mxgraph.examples.web; + +import org.mortbay.jetty.Handler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.handler.HandlerList; +import org.mortbay.jetty.handler.ResourceHandler; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; + +/** + * The save servlet is used to echo XML to the client, eg. for SVG export and saving + * (see Dialogs.js:SaveDialog and ExportDialog). The export servlet is used to + * implement image and PDF export (see Dialogs.js:ExportDialog). Note that the + * CSS support is limited to the following for all HTML markup: + * http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html + * The open servlet is used to open files. It does this by calling some JavaScript + * hook in the client-side page (see open.html). + */ +public class GraphEditor +{ + + public static int PORT = 8080; + + /** + * Uncomment this for better font size rendering in px units within labels. + */ + static + { +// mxGraphicsCanvas2D.HTML_SCALE = 0.75; +// mxGraphicsCanvas2D.HTML_UNIT = "px"; + } + + /** + * Point your browser to http://localhost:8080/javascript/examples/grapheditor/www/index.html + */ + public static void main(String[] args) throws Exception + { + Server server = new Server(PORT); + + // Servlets + Context context = new Context(server, "/"); + context.addServlet(new ServletHolder(new EchoServlet()), "/save"); + context.addServlet(new ServletHolder(new ExportServlet()), "/export"); + context.addServlet(new ServletHolder(new OpenServlet()), "/open"); + + ResourceHandler fileHandler = new ResourceHandler(); + fileHandler.setResourceBase("."); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { fileHandler, context }); + server.setHandler(handlers); + + System.out.println("Go to http://localhost:" + PORT + "/javascript/examples/grapheditor/www/index.html"); + + server.start(); + server.join(); + } +} diff --git a/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/OpenServlet.java b/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/OpenServlet.java new file mode 100644 index 0000000..8daa089 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/java/src/com/mxgraph/examples/web/OpenServlet.java @@ -0,0 +1,304 @@ +/** + * Copyright (c) 2011-2012, JGraph Ltd + */ +package com.mxgraph.examples.web; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Servlet implementation class OpenServlet. + * + * open.html implements the user interface. This file is displayed within an + * IFRAME in order to better handle the response. The form is then processed + * either locally if the browser implements the HTML5 FileReader API or via the + * OpenServlet. Note that the mechanism to open files uses OpenFile in + * Editor.js, as well as Editor.openFile when the client starts. This is + * required to abstract away the asynchronous loading of the new editor and + * handling of the response, which in turn calls the setData method on the + * OpenFile instance of the parent window of the frame where open.html was + * displayed (see below). + */ +public class OpenServlet extends HttpServlet +{ + + /** + * + */ + private static final long serialVersionUID = -4442397463551836919L; + + /** + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) + */ + protected void doPost(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException + { + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/html; charset=UTF-8"); + + OutputStream out = response.getOutputStream(); + String encoding = request.getHeader("Accept-Encoding"); + + // Supports GZIP content encoding + if (encoding != null && encoding.indexOf("gzip") >= 0) + { + response.setHeader("Content-Encoding", "gzip"); + out = new GZIPOutputStream(out); + } + + PrintWriter writer = new PrintWriter(out); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + + writer.flush(); + writer.close(); + } + + public static void error(PrintWriter w, String key) + { + w.println("window.parent.openFile.error(window.parent.mxResources.get('" + + key + "'));"); + } + + /** + * Encodes the passed String as UTF-8 using an algorithm that's compatible + * with JavaScript's encodeURIComponent function. Returns + * null if the String is null. + * + * @param s The String to be encoded + * @return the encoded String + */ + public static String encodeURIComponent(String s) + { + String result = null; + + try + { + result = URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20") + .replaceAll("\\%21", "!").replaceAll("\\%28", "(") + .replaceAll("\\%29", ")").replaceAll("\\%7E", "~"); + } + + // This exception should never occur. + catch (UnsupportedEncodingException e) + { + result = s; + } + + return result; + } + + /////////////////////////////////////////////////////////////////////// + // Handling of multipart/form-data **** NOT FOR PRODUCTION USE! **** // + // If you want a production library, we recommend Commons-Fileupload // + // https://commons.apache.org/proper/commons-fileupload/ // + /////////////////////////////////////////////////////////////////////// + + /** + * Encoding for the multipart/form-data. + */ + protected static final String ENCODING = "ISO-8859-1"; + + /** + * Parses the given multipart/form-data request into a map that maps from + * names to values. Note that this implementation ignores the file type and + * filename and does only return the actual data as the value for the name + * of the file input in the form. Returns an empty map if the form does not + * contain any multipart/form-data. + */ + protected Map parseMultipartRequest( + HttpServletRequest request) throws IOException + { + Map result = new Hashtable(); + String contentType = request.getHeader("Content-Type"); + + // Checks if the form is of the correct content type + if (contentType != null + && contentType.indexOf("multipart/form-data") == 0) + { + // Extracts the boundary from the header + int boundaryIndex = contentType.indexOf("boundary="); + String boundary = "--" + + contentType.substring(boundaryIndex + 9).trim(); + + // Splits the multipart/form-data into its different parts + Iterator it = splitFormData( + readStream(request.getInputStream()), boundary).iterator(); + + while (it.hasNext()) + { + parsePart(it.next(), result); + } + } + + return result; + } + + /** + * Parses the values in the given form-data part into the given map. The + * value of the name attribute will be used as the name for the data. The + * filename will be stored under filename in the given map and the + * content-type is ignored in this implementation. + */ + protected void parsePart(String part, Map into) + { + String[] lines = part.split("\r\n"); + + if (lines.length > 1) + { + // First line contains content-disposition in the following format: + // form-data; name="upfile"; filename="avatar.jpg" + String[] tokens = lines[1].split(";"); + + // Stores the value of the name attribute for the form-data + String name = null; + + for (int i = 0; i < tokens.length; i++) + { + String tmp = tokens[i]; + int index = tmp.indexOf("="); + + // Checks if the token contains a key=value pair + if (index >= 0) + { + String key = tmp.substring(0, index).trim(); + String value = tmp.substring(index + 2, tmp.length() - 1); + + if (key.equals("name")) + { + name = value; + } + else + { + into.put(key, value); + } + } + } + + // Parses all lines starting from the first empty line + if (name != null && lines.length > 2) + { + boolean active = false; + StringBuffer value = new StringBuffer(); + + for (int i = 2; i < lines.length; i++) + { + if (active) + { + value.append(lines[i]); + } + else if (!active) + { + active = lines[i].length() == 0; + } + } + + into.put(name, value.toString()); + } + } + } + + /** + * Returns the parts of the given multipart/form-data. + */ + protected List splitFormData(String formData, String boundary) + { + List result = new LinkedList(); + int nextBoundary = formData.indexOf(boundary); + + while (nextBoundary >= 0) + { + if (nextBoundary > 0) + { + result.add(formData.substring(0, nextBoundary)); + } + + formData = formData.substring(nextBoundary + boundary.length()); + nextBoundary = formData.indexOf(boundary); + } + + return result; + } + + /** + * Reads the complete stream into memory as a String. + */ + protected String readStream(InputStream is) throws IOException + { + if (is != null) + { + StringBuffer buffer = new StringBuffer(); + try + { + Reader in = new BufferedReader(new InputStreamReader(is, + ENCODING)); + int ch; + + while ((ch = in.read()) > -1) + { + buffer.append((char) ch); + } + } + finally + { + is.close(); + } + + return buffer.toString(); + } + else + { + return ""; + } + } + +} diff --git a/static/mxgraph/examples/grapheditor/www/deflate/base64.js b/static/mxgraph/examples/grapheditor/www/deflate/base64.js new file mode 100644 index 0000000..1e870ac --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/deflate/base64.js @@ -0,0 +1,151 @@ + +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input, binary) { + binary = (binary != null) ? binary : false; + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + if (!binary) + { + input = Base64._utf8_encode(input); + } + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input, binary) { + binary = (binary != null) ? binary : false; + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + if (!binary) + { + output = Base64._utf8_decode(output); + } + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} diff --git a/static/mxgraph/examples/grapheditor/www/deflate/pako.min.js b/static/mxgraph/examples/grapheditor/www/deflate/pako.min.js new file mode 100644 index 0000000..ba39731 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/deflate/pako.min.js @@ -0,0 +1 @@ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).pako=t()}}(function(){return function r(s,o,l){function h(e,t){if(!o[e]){if(!s[e]){var a="function"==typeof require&&require;if(!t&&a)return a(e,!0);if(d)return d(e,!0);var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}var n=o[e]={exports:{}};s[e][0].call(n.exports,function(t){return h(s[e][1][t]||t)},n,n.exports,r,s,o,l)}return o[e].exports}for(var d="function"==typeof require&&require,t=0;t>>6:(a<65536?e[r++]=224|a>>>12:(e[r++]=240|a>>>18,e[r++]=128|a>>>12&63),e[r++]=128|a>>>6&63),e[r++]=128|63&a);return e},a.buf2binstring=function(t){return d(t,t.length)},a.binstring2buf=function(t){for(var e=new l.Buf8(t.length),a=0,i=e.length;a>10&1023,o[i++]=56320|1023&n)}return d(o,i)},a.utf8border=function(t,e){var a;for((e=e||t.length)>t.length&&(e=t.length),a=e-1;0<=a&&128==(192&t[a]);)a--;return a<0?e:0===a?e:a+h[t[a]]>e?a:e}},{"./common":3}],5:[function(t,e,a){"use strict";e.exports=function(t,e,a,i){for(var n=65535&t|0,r=t>>>16&65535|0,s=0;0!==a;){for(a-=s=2e3>>1:t>>>1;e[a]=t}return e}();e.exports=function(t,e,a,i){var n=o,r=i+a;t^=-1;for(var s=i;s>>8^n[255&(t^e[s])];return-1^t}},{}],8:[function(t,e,a){"use strict";var l,_=t("../utils/common"),h=t("./trees"),u=t("./adler32"),c=t("./crc32"),i=t("./messages"),d=0,f=4,b=0,g=-2,m=-1,w=4,n=2,p=8,v=9,r=286,s=30,o=19,k=2*r+1,y=15,x=3,z=258,B=z+x+1,S=42,E=113,A=1,Z=2,R=3,C=4;function N(t,e){return t.msg=i[e],e}function O(t){return(t<<1)-(4t.avail_out&&(a=t.avail_out),0!==a&&(_.arraySet(t.output,e.pending_buf,e.pending_out,a,t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))}function U(t,e){h._tr_flush_block(t,0<=t.block_start?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,I(t.strm)}function T(t,e){t.pending_buf[t.pending++]=e}function F(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function L(t,e){var a,i,n=t.max_chain_length,r=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-B?t.strstart-(t.w_size-B):0,h=t.window,d=t.w_mask,f=t.prev,_=t.strstart+z,u=h[r+s-1],c=h[r+s];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do{if(h[(a=e)+s]===c&&h[a+s-1]===u&&h[a]===h[r]&&h[++a]===h[r+1]){r+=2,a++;do{}while(h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&r<_);if(i=z-(_-r),r=_-z,sl&&0!=--n);return s<=t.lookahead?s:t.lookahead}function H(t){var e,a,i,n,r,s,o,l,h,d,f=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=f+(f-B)){for(_.arraySet(t.window,t.window,f,f,0),t.match_start-=f,t.strstart-=f,t.block_start-=f,e=a=t.hash_size;i=t.head[--e],t.head[e]=f<=i?i-f:0,--a;);for(e=a=f;i=t.prev[--e],t.prev[e]=f<=i?i-f:0,--a;);n+=f}if(0===t.strm.avail_in)break;if(s=t.strm,o=t.window,l=t.strstart+t.lookahead,h=n,d=void 0,d=s.avail_in,h=x)for(r=t.strstart-t.insert,t.ins_h=t.window[r],t.ins_h=(t.ins_h<=x&&(t.ins_h=(t.ins_h<=x)if(i=h._tr_tally(t,t.strstart-t.match_start,t.match_length-x),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=x){for(t.match_length--;t.strstart++,t.ins_h=(t.ins_h<=x&&(t.ins_h=(t.ins_h<=x&&t.match_length<=t.prev_length){for(n=t.strstart+t.lookahead-x,i=h._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-x),t.lookahead-=t.prev_length-1,t.prev_length-=2;++t.strstart<=n&&(t.ins_h=(t.ins_h<t.pending_buf_size-5&&(a=t.pending_buf_size-5);;){if(t.lookahead<=1){if(H(t),0===t.lookahead&&e===d)return A;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+a;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,U(t,!1),0===t.strm.avail_out))return A;if(t.strstart-t.block_start>=t.w_size-B&&(U(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(U(t,!0),0===t.strm.avail_out?R:C):(t.strstart>t.block_start&&(U(t,!1),t.strm.avail_out),A)}),new M(4,4,8,4,j),new M(4,5,16,8,j),new M(4,6,32,32,j),new M(4,4,16,16,K),new M(8,16,32,32,K),new M(8,16,128,128,K),new M(8,32,128,256,K),new M(32,128,258,1024,K),new M(32,258,258,4096,K)],a.deflateInit=function(t,e){return G(t,e,p,15,8,0)},a.deflateInit2=G,a.deflateReset=q,a.deflateResetKeep=Y,a.deflateSetHeader=function(t,e){return t&&t.state?2!==t.state.wrap?g:(t.state.gzhead=e,b):g},a.deflate=function(t,e){var a,i,n,r;if(!t||!t.state||5>8&255),T(i,i.gzhead.time>>16&255),T(i,i.gzhead.time>>24&255),T(i,9===i.level?2:2<=i.strategy||i.level<2?4:0),T(i,255&i.gzhead.os),i.gzhead.extra&&i.gzhead.extra.length&&(T(i,255&i.gzhead.extra.length),T(i,i.gzhead.extra.length>>8&255)),i.gzhead.hcrc&&(t.adler=c(t.adler,i.pending_buf,i.pending,0)),i.gzindex=0,i.status=69):(T(i,0),T(i,0),T(i,0),T(i,0),T(i,0),T(i,9===i.level?2:2<=i.strategy||i.level<2?4:0),T(i,3),i.status=E);else{var s=p+(i.w_bits-8<<4)<<8;s|=(2<=i.strategy||i.level<2?0:i.level<6?1:6===i.level?2:3)<<6,0!==i.strstart&&(s|=32),s+=31-s%31,i.status=E,F(i,s),0!==i.strstart&&(F(i,t.adler>>>16),F(i,65535&t.adler)),t.adler=1}if(69===i.status)if(i.gzhead.extra){for(n=i.pending;i.gzindex<(65535&i.gzhead.extra.length)&&(i.pending!==i.pending_buf_size||(i.gzhead.hcrc&&i.pending>n&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),I(t),n=i.pending,i.pending!==i.pending_buf_size));)T(i,255&i.gzhead.extra[i.gzindex]),i.gzindex++;i.gzhead.hcrc&&i.pending>n&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),i.gzindex===i.gzhead.extra.length&&(i.gzindex=0,i.status=73)}else i.status=73;if(73===i.status)if(i.gzhead.name){n=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>n&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),I(t),n=i.pending,i.pending===i.pending_buf_size)){r=1;break}T(i,r=i.gzindexn&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),0===r&&(i.gzindex=0,i.status=91)}else i.status=91;if(91===i.status)if(i.gzhead.comment){n=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>n&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),I(t),n=i.pending,i.pending===i.pending_buf_size)){r=1;break}T(i,r=i.gzindexn&&(t.adler=c(t.adler,i.pending_buf,i.pending-n,n)),0===r&&(i.status=103)}else i.status=103;if(103===i.status&&(i.gzhead.hcrc?(i.pending+2>i.pending_buf_size&&I(t),i.pending+2<=i.pending_buf_size&&(T(i,255&t.adler),T(i,t.adler>>8&255),t.adler=0,i.status=E)):i.status=E),0!==i.pending){if(I(t),0===t.avail_out)return i.last_flush=-1,b}else if(0===t.avail_in&&O(e)<=O(a)&&e!==f)return N(t,-5);if(666===i.status&&0!==t.avail_in)return N(t,-5);if(0!==t.avail_in||0!==i.lookahead||e!==d&&666!==i.status){var o=2===i.strategy?function(t,e){for(var a;;){if(0===t.lookahead&&(H(t),0===t.lookahead)){if(e===d)return A;break}if(t.match_length=0,a=h._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(U(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(U(t,!0),0===t.strm.avail_out?R:C):t.last_lit&&(U(t,!1),0===t.strm.avail_out)?A:Z}(i,e):3===i.strategy?function(t,e){for(var a,i,n,r,s=t.window;;){if(t.lookahead<=z){if(H(t),t.lookahead<=z&&e===d)return A;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=x&&0t.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=x?(a=h._tr_tally(t,1,t.match_length-x),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=h._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(U(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(U(t,!0),0===t.strm.avail_out?R:C):t.last_lit&&(U(t,!1),0===t.strm.avail_out)?A:Z}(i,e):l[i.level].func(i,e);if(o!==R&&o!==C||(i.status=666),o===A||o===R)return 0===t.avail_out&&(i.last_flush=-1),b;if(o===Z&&(1===e?h._tr_align(i):5!==e&&(h._tr_stored_block(i,0,0,!1),3===e&&(D(i.head),0===i.lookahead&&(i.strstart=0,i.block_start=0,i.insert=0))),I(t),0===t.avail_out))return i.last_flush=-1,b}return e!==f?b:i.wrap<=0?1:(2===i.wrap?(T(i,255&t.adler),T(i,t.adler>>8&255),T(i,t.adler>>16&255),T(i,t.adler>>24&255),T(i,255&t.total_in),T(i,t.total_in>>8&255),T(i,t.total_in>>16&255),T(i,t.total_in>>24&255)):(F(i,t.adler>>>16),F(i,65535&t.adler)),I(t),0=a.w_size&&(0===r&&(D(a.head),a.strstart=0,a.block_start=0,a.insert=0),h=new _.Buf8(a.w_size),_.arraySet(h,e,d-a.w_size,a.w_size,0),e=h,d=a.w_size),s=t.avail_in,o=t.next_in,l=t.input,t.avail_in=d,t.next_in=0,t.input=e,H(a);a.lookahead>=x;){for(i=a.strstart,n=a.lookahead-(x-1);a.ins_h=(a.ins_h<>>=v=p>>>24,c-=v,0===(v=p>>>16&255))S[r++]=65535&p;else{if(!(16&v)){if(0==(64&v)){p=b[(65535&p)+(u&(1<>>=v,c-=v),c<15&&(u+=B[i++]<>>=v=p>>>24,c-=v,!(16&(v=p>>>16&255))){if(0==(64&v)){p=g[(65535&p)+(u&(1<>>=v,c-=v,(v=r-s)>3,u&=(1<<(c-=k<<3))-1,t.next_in=i,t.next_out=r,t.avail_in=i>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function r(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new Z.Buf16(320),this.work=new Z.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function s(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=F,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new Z.Buf32(i),e.distcode=e.distdyn=new Z.Buf32(n),e.sane=1,e.back=-1,U):T}function o(t){var e;return t&&t.state?((e=t.state).wsize=0,e.whave=0,e.wnext=0,s(t)):T}function l(t,e){var a,i;return t&&t.state?(i=t.state,e<0?(a=0,e=-e):(a=1+(e>>4),e<48&&(e&=15)),e&&(e<8||15=r.wsize?(Z.arraySet(r.window,e,a-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(i<(n=r.wsize-r.wnext)&&(n=i),Z.arraySet(r.window,e,a-i,n,r.wnext),(i-=n)?(Z.arraySet(r.window,e,a-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=n,r.wnext===r.wsize&&(r.wnext=0),r.whave>>8&255,a.check=C(a.check,E,2,0),d=h=0,a.mode=2;break}if(a.flags=0,a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&h)<<8)+(h>>8))%31){t.msg="incorrect header check",a.mode=30;break}if(8!=(15&h)){t.msg="unknown compression method",a.mode=30;break}if(d-=4,y=8+(15&(h>>>=4)),0===a.wbits)a.wbits=y;else if(y>a.wbits){t.msg="invalid window size",a.mode=30;break}a.dmax=1<>8&1),512&a.flags&&(E[0]=255&h,E[1]=h>>>8&255,a.check=C(a.check,E,2,0)),d=h=0,a.mode=3;case 3:for(;d<32;){if(0===o)break t;o--,h+=i[r++]<>>8&255,E[2]=h>>>16&255,E[3]=h>>>24&255,a.check=C(a.check,E,4,0)),d=h=0,a.mode=4;case 4:for(;d<16;){if(0===o)break t;o--,h+=i[r++]<>8),512&a.flags&&(E[0]=255&h,E[1]=h>>>8&255,a.check=C(a.check,E,2,0)),d=h=0,a.mode=5;case 5:if(1024&a.flags){for(;d<16;){if(0===o)break t;o--,h+=i[r++]<>>8&255,a.check=C(a.check,E,2,0)),d=h=0}else a.head&&(a.head.extra=null);a.mode=6;case 6:if(1024&a.flags&&(o<(u=a.length)&&(u=o),u&&(a.head&&(y=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Array(a.head.extra_len)),Z.arraySet(a.head.extra,i,r,u,y)),512&a.flags&&(a.check=C(a.check,i,u,r)),o-=u,r+=u,a.length-=u),a.length))break t;a.length=0,a.mode=7;case 7:if(2048&a.flags){if(0===o)break t;for(u=0;y=i[r+u++],a.head&&y&&a.length<65536&&(a.head.name+=String.fromCharCode(y)),y&&u>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=12;break;case 10:for(;d<32;){if(0===o)break t;o--,h+=i[r++]<>>=7&d,d-=7&d,a.mode=27;break}for(;d<3;){if(0===o)break t;o--,h+=i[r++]<>>=1)){case 0:a.mode=14;break;case 1:if(H(a),a.mode=20,6!==e)break;h>>>=2,d-=2;break t;case 2:a.mode=17;break;case 3:t.msg="invalid block type",a.mode=30}h>>>=2,d-=2;break;case 14:for(h>>>=7&d,d-=7&d;d<32;){if(0===o)break t;o--,h+=i[r++]<>>16^65535)){t.msg="invalid stored block lengths",a.mode=30;break}if(a.length=65535&h,d=h=0,a.mode=15,6===e)break t;case 15:a.mode=16;case 16:if(u=a.length){if(o>>=5,d-=5,a.ndist=1+(31&h),h>>>=5,d-=5,a.ncode=4+(15&h),h>>>=4,d-=4,286>>=3,d-=3}for(;a.have<19;)a.lens[A[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,z={bits:a.lenbits},x=O(0,a.lens,0,19,a.lencode,0,a.work,z),a.lenbits=z.bits,x){t.msg="invalid code lengths set",a.mode=30;break}a.have=0,a.mode=19;case 19:for(;a.have>>16&255,w=65535&S,!((g=S>>>24)<=d);){if(0===o)break t;o--,h+=i[r++]<>>=g,d-=g,a.lens[a.have++]=w;else{if(16===w){for(B=g+2;d>>=g,d-=g,0===a.have){t.msg="invalid bit length repeat",a.mode=30;break}y=a.lens[a.have-1],u=3+(3&h),h>>>=2,d-=2}else if(17===w){for(B=g+3;d>>=g)),h>>>=3,d-=3}else{for(B=g+7;d>>=g)),h>>>=7,d-=7}if(a.have+u>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=30;break}for(;u--;)a.lens[a.have++]=y}}if(30===a.mode)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=30;break}if(a.lenbits=9,z={bits:a.lenbits},x=O(D,a.lens,0,a.nlen,a.lencode,0,a.work,z),a.lenbits=z.bits,x){t.msg="invalid literal/lengths set",a.mode=30;break}if(a.distbits=6,a.distcode=a.distdyn,z={bits:a.distbits},x=O(I,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,z),a.distbits=z.bits,x){t.msg="invalid distances set",a.mode=30;break}if(a.mode=20,6===e)break t;case 20:a.mode=21;case 21:if(6<=o&&258<=l){t.next_out=s,t.avail_out=l,t.next_in=r,t.avail_in=o,a.hold=h,a.bits=d,N(t,_),s=t.next_out,n=t.output,l=t.avail_out,r=t.next_in,i=t.input,o=t.avail_in,h=a.hold,d=a.bits,12===a.mode&&(a.back=-1);break}for(a.back=0;m=(S=a.lencode[h&(1<>>16&255,w=65535&S,!((g=S>>>24)<=d);){if(0===o)break t;o--,h+=i[r++]<>p)])>>>16&255,w=65535&S,!(p+(g=S>>>24)<=d);){if(0===o)break t;o--,h+=i[r++]<>>=p,d-=p,a.back+=p}if(h>>>=g,d-=g,a.back+=g,a.length=w,0===m){a.mode=26;break}if(32&m){a.back=-1,a.mode=12;break}if(64&m){t.msg="invalid literal/length code",a.mode=30;break}a.extra=15&m,a.mode=22;case 22:if(a.extra){for(B=a.extra;d>>=a.extra,d-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=23;case 23:for(;m=(S=a.distcode[h&(1<>>16&255,w=65535&S,!((g=S>>>24)<=d);){if(0===o)break t;o--,h+=i[r++]<>p)])>>>16&255,w=65535&S,!(p+(g=S>>>24)<=d);){if(0===o)break t;o--,h+=i[r++]<>>=p,d-=p,a.back+=p}if(h>>>=g,d-=g,a.back+=g,64&m){t.msg="invalid distance code",a.mode=30;break}a.offset=w,a.extra=15&m,a.mode=24;case 24:if(a.extra){for(B=a.extra;d>>=a.extra,d-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=30;break}a.mode=25;case 25:if(0===l)break t;if(u=_-l,a.offset>u){if((u=a.offset-u)>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=30;break}u>a.wnext?(u-=a.wnext,c=a.wsize-u):c=a.wnext-u,u>a.length&&(u=a.length),b=a.window}else b=n,c=s-a.offset,u=a.length;for(lu?(b=N[O+s[p]],g=A[Z+s[p]]):(b=96,g=0),l=1<>z)+(h-=l)]=c<<24|b<<16|g|0,0!==h;);for(l=1<>=1;if(0!==l?(E&=l-1,E+=l):E=0,p++,0==--R[w]){if(w===k)break;w=e[a+s[p]]}if(y>>7)]}function T(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function F(t,e,a){t.bi_valid>n-a?(t.bi_buf|=e<>n-t.bi_valid,t.bi_valid+=a-n):(t.bi_buf|=e<>>=1,a<<=1,0<--e;);return a>>>1}function j(t,e,a){var i,n,r=new Array(m+1),s=0;for(i=1;i<=m;i++)r[i]=s=s+a[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=H(r[o]++,o))}}function K(t){var e;for(e=0;e<_;e++)t.dyn_ltree[2*e]=0;for(e=0;e>1;1<=a;a--)Y(t,r,a);for(n=l;a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],Y(t,r,1),i=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=i,r[2*n]=r[2*a]+r[2*i],t.depth[n]=(t.depth[a]>=t.depth[i]?t.depth[a]:t.depth[i])+1,r[2*a+1]=r[2*i+1]=n,t.heap[1]=n++,Y(t,r,1),2<=t.heap_len;);t.heap[--t.heap_max]=t.heap[1],function(t,e){var a,i,n,r,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,_=e.stat_desc.extra_bits,u=e.stat_desc.extra_base,c=e.stat_desc.max_length,b=0;for(r=0;r<=m;r++)t.bl_count[r]=0;for(l[2*t.heap[t.heap_max]+1]=0,a=t.heap_max+1;a>=7;i>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return o;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return h;for(e=32;e>>3,(r=t.static_len+3+7>>>3)<=n&&(n=r)):n=r=a+5,a+4<=n&&-1!==e?Q(t,e,a,i):4===t.strategy||r===n?(F(t,2+(i?1:0),3),q(t,S,E)):(F(t,4+(i?1:0),3),function(t,e,a,i){var n;for(F(t,e-257,5),F(t,a-1,5),F(t,i-4,4),n=0;n>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&a,t.last_lit++,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(Z[a]+f+1)]++,t.dyn_dtree[2*U(e)]++),t.last_lit===t.lit_bufsize-1},a._tr_align=function(t){var e;F(t,2,3),L(t,w,S),16===(e=t).bi_valid?(T(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}},{"../utils/common":3}],15:[function(t,e,a){"use strict";e.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],"/":[function(t,e,a){"use strict";var i={};(0,t("./lib/utils/common").assign)(i,t("./lib/deflate"),t("./lib/inflate"),t("./lib/zlib/constants")),e.exports=i},{"./lib/deflate":1,"./lib/inflate":2,"./lib/utils/common":3,"./lib/zlib/constants":6}]},{},[])("/")}); diff --git a/static/mxgraph/examples/grapheditor/www/images/checkmark.gif b/static/mxgraph/examples/grapheditor/www/images/checkmark.gif new file mode 100644 index 0000000..d79444d Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/checkmark.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/clear.gif b/static/mxgraph/examples/grapheditor/www/images/clear.gif new file mode 100644 index 0000000..c6acf0a Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/clear.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/close.png b/static/mxgraph/examples/grapheditor/www/images/close.png new file mode 100644 index 0000000..d319efb Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/close.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/collapsed.gif b/static/mxgraph/examples/grapheditor/www/images/collapsed.gif new file mode 100644 index 0000000..ce97774 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/collapsed.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/dropdown.gif b/static/mxgraph/examples/grapheditor/www/images/dropdown.gif new file mode 100644 index 0000000..9c94ea5 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/dropdown.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/dropdown.png b/static/mxgraph/examples/grapheditor/www/images/dropdown.png new file mode 100644 index 0000000..0aeac2d Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/dropdown.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/edit.gif b/static/mxgraph/examples/grapheditor/www/images/edit.gif new file mode 100644 index 0000000..3c07607 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/edit.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/expanded.gif b/static/mxgraph/examples/grapheditor/www/images/expanded.gif new file mode 100644 index 0000000..461297f Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/expanded.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/grid.gif b/static/mxgraph/examples/grapheditor/www/images/grid.gif new file mode 100644 index 0000000..f4e7063 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/grid.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/handle-fixed.png b/static/mxgraph/examples/grapheditor/www/images/handle-fixed.png new file mode 100644 index 0000000..b4b600b Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/handle-fixed.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/handle-main.png b/static/mxgraph/examples/grapheditor/www/images/handle-main.png new file mode 100644 index 0000000..ee067ff Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/handle-main.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/handle-rotate.png b/static/mxgraph/examples/grapheditor/www/images/handle-rotate.png new file mode 100644 index 0000000..dcb0e5e Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/handle-rotate.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/handle-secondary.png b/static/mxgraph/examples/grapheditor/www/images/handle-secondary.png new file mode 100644 index 0000000..b4a3090 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/handle-secondary.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/handle-terminal.png b/static/mxgraph/examples/grapheditor/www/images/handle-terminal.png new file mode 100644 index 0000000..ec03b31 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/handle-terminal.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/help.png b/static/mxgraph/examples/grapheditor/www/images/help.png new file mode 100644 index 0000000..17ee2eb Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/help.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/locked.png b/static/mxgraph/examples/grapheditor/www/images/locked.png new file mode 100644 index 0000000..8dbac82 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/locked.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/logo.png b/static/mxgraph/examples/grapheditor/www/images/logo.png new file mode 100644 index 0000000..053a1eb Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/logo.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/move.png b/static/mxgraph/examples/grapheditor/www/images/move.png new file mode 100644 index 0000000..9f4bc91 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/move.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/nocolor.png b/static/mxgraph/examples/grapheditor/www/images/nocolor.png new file mode 100644 index 0000000..aec4534 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/nocolor.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/refresh.png b/static/mxgraph/examples/grapheditor/www/images/refresh.png new file mode 100644 index 0000000..e1d0c6a Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/refresh.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/round-drop.png b/static/mxgraph/examples/grapheditor/www/images/round-drop.png new file mode 100644 index 0000000..b5e2fb6 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/round-drop.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/search.png b/static/mxgraph/examples/grapheditor/www/images/search.png new file mode 100644 index 0000000..d763a72 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/search.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/transparent.gif b/static/mxgraph/examples/grapheditor/www/images/transparent.gif new file mode 100644 index 0000000..76040f2 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/transparent.gif differ diff --git a/static/mxgraph/examples/grapheditor/www/images/triangle-down.png b/static/mxgraph/examples/grapheditor/www/images/triangle-down.png new file mode 100644 index 0000000..d03b98f Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/triangle-down.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/triangle-left.png b/static/mxgraph/examples/grapheditor/www/images/triangle-left.png new file mode 100644 index 0000000..44b8425 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/triangle-left.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/triangle-right.png b/static/mxgraph/examples/grapheditor/www/images/triangle-right.png new file mode 100644 index 0000000..9865628 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/triangle-right.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/triangle-up.png b/static/mxgraph/examples/grapheditor/www/images/triangle-up.png new file mode 100644 index 0000000..6d93676 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/triangle-up.png differ diff --git a/static/mxgraph/examples/grapheditor/www/images/unlocked.png b/static/mxgraph/examples/grapheditor/www/images/unlocked.png new file mode 100644 index 0000000..5ea44c1 Binary files /dev/null and b/static/mxgraph/examples/grapheditor/www/images/unlocked.png differ diff --git a/static/mxgraph/examples/grapheditor/www/index.html b/static/mxgraph/examples/grapheditor/www/index.html new file mode 100644 index 0000000..b297369 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/index.html @@ -0,0 +1,111 @@ + + + + + Grapheditor + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/mxgraph/examples/grapheditor/www/js/Actions.js b/static/mxgraph/examples/grapheditor/www/js/Actions.js new file mode 100644 index 0000000..608a5a7 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/Actions.js @@ -0,0 +1,1511 @@ +/** + * Copyright (c) 2006-2020, JGraph Ltd + * Copyright (c) 2006-2020, draw.io AG + * + * Constructs the actions object for the given UI. + */ +function Actions(editorUi) +{ + this.editorUi = editorUi; + this.actions = new Object(); + this.init(); +}; + +/** + * Adds the default actions. + */ +Actions.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var isGraphEnabled = function() + { + return Action.prototype.isEnabled.apply(this, arguments) && graph.isEnabled(); + }; + + // File actions + this.addAction('new...', function() { graph.openLink(ui.getUrl()); }); + this.addAction('open...', function() + { + window.openNew = true; + window.openKey = 'open'; + + ui.openFile(); + }); + this.addAction('import...', function() + { + window.openNew = false; + window.openKey = 'import'; + + // Closes dialog after open + window.openFile = new OpenFile(mxUtils.bind(this, function() + { + ui.hideDialog(); + })); + + window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) + { + try + { + var doc = mxUtils.parseXml(xml); + editor.graph.setSelectionCells(editor.graph.importGraphModel(doc.documentElement)); + } + catch (e) + { + mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); + } + })); + + // Removes openFile if dialog is closed + ui.showDialog(new OpenDialog(this).container, 320, 220, true, true, function() + { + window.openFile = null; + }); + }).isEnabled = isGraphEnabled; + this.addAction('save', function() { ui.saveFile(false); }, null, null, Editor.ctrlKey + '+S').isEnabled = isGraphEnabled; + this.addAction('saveAs...', function() { ui.saveFile(true); }, null, null, Editor.ctrlKey + '+Shift+S').isEnabled = isGraphEnabled; + this.addAction('export...', function() { ui.showDialog(new ExportDialog(ui).container, 300, 296, true, true); }); + this.addAction('editDiagram...', function() + { + var dlg = new EditDiagramDialog(ui); + ui.showDialog(dlg.container, 620, 420, true, false); + dlg.init(); + }); + this.addAction('pageSetup...', function() { ui.showDialog(new PageSetupDialog(ui).container, 320, 220, true, true); }).isEnabled = isGraphEnabled; + this.addAction('print...', function() { ui.showDialog(new PrintDialog(ui).container, 300, 180, true, true); }, null, 'sprite-print', Editor.ctrlKey + '+P'); + this.addAction('preview', function() { mxUtils.show(graph, null, 10, 10); }); + + // Edit actions + this.addAction('undo', function() { ui.undo(); }, null, 'sprite-undo', Editor.ctrlKey + '+Z'); + this.addAction('redo', function() { ui.redo(); }, null, 'sprite-redo', (!mxClient.IS_WIN) ? Editor.ctrlKey + '+Shift+Z' : Editor.ctrlKey + '+Y'); + this.addAction('workflow', function() { ui.showDialog(new WorkFlowDialog(ui).container, 600, 800, true, true); }, null, 'sprite-redo', Editor.ctrlKey + '+W'); + this.addAction('cut', function() { mxClipboard.cut(graph); }, null, 'sprite-cut', Editor.ctrlKey + '+X'); + this.addAction('copy', function() + { + try + { + mxClipboard.copy(graph); + } + catch (e) + { + ui.handleError(e); + } + }, null, 'sprite-copy', Editor.ctrlKey + '+C'); + this.addAction('paste', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + mxClipboard.paste(graph); + } + }, false, 'sprite-paste', Editor.ctrlKey + '+V'); + this.addAction('pasteHere', function(evt) + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.getModel().beginUpdate(); + try + { + var cells = mxClipboard.paste(graph); + + if (cells != null) + { + var includeEdges = true; + + for (var i = 0; i < cells.length && includeEdges; i++) + { + includeEdges = includeEdges && graph.model.isEdge(cells[i]); + } + + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + var bb = null; + + if (cells.length == 1 && includeEdges) + { + var geo = graph.getCellGeometry(cells[0]); + + if (geo != null) + { + bb = geo.getTerminalPoint(true); + } + } + + bb = (bb != null) ? bb : graph.getBoundingBoxFromGeometry(cells, includeEdges); + + if (bb != null) + { + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + graph.cellsMoved(cells, x - bb.x, y - bb.y); + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + + this.addAction('copySize', function(evt) + { + var cell = graph.getSelectionCell(); + + if (graph.isEnabled() && cell != null && graph.getModel().isVertex(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + ui.copiedSize = new mxRectangle(geo.x, geo.y, geo.width, geo.height); + } + } + }, null, null, 'Alt+Shift+X'); + + this.addAction('pasteSize', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty() && ui.copiedSize != null) + { + graph.getModel().beginUpdate(); + + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + geo.width = ui.copiedSize.width; + geo.height = ui.copiedSize.height; + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shift+V'); + + function deleteCells(includeEdges) + { + // Cancels interactive operations + graph.escape(); + var cells = graph.getDeletableCells(graph.getSelectionCells()); + + if (cells != null && cells.length > 0) + { + var parents = (graph.selectParentAfterDelete) ? graph.model.getParents(cells) : null; + graph.removeCells(cells, includeEdges); + + // Selects parents for easier editing of groups + if (parents != null) + { + var select = []; + + for (var i = 0; i < parents.length; i++) + { + if (graph.model.contains(parents[i]) && + (graph.model.isVertex(parents[i]) || + graph.model.isEdge(parents[i]))) + { + select.push(parents[i]); + } + } + + graph.setSelectionCells(select); + } + } + }; + + this.addAction('delete', function(evt) + { + deleteCells(evt != null && mxEvent.isShiftDown(evt)); + }, null, null, 'Delete'); + this.addAction('deleteAll', function() + { + deleteCells(true); + }, null, null, Editor.ctrlKey + '+Delete'); + this.addAction('duplicate', function() + { + try + { + graph.setSelectionCells(graph.duplicateCells()); + } + catch (e) + { + ui.handleError(e); + } + }, null, null, Editor.ctrlKey + '+D'); + this.put('turn', new Action(mxResources.get('turn') + ' / ' + mxResources.get('reverse'), function(evt) + { + graph.turnShapes(graph.getSelectionCells(), (evt != null) ? mxEvent.isShiftDown(evt) : false); + }, null, null, Editor.ctrlKey + '+R')); + this.addAction('selectVertices', function() { graph.selectVertices(null, true); }, null, null, Editor.ctrlKey + '+Shift+I'); + this.addAction('selectEdges', function() { graph.selectEdges(); }, null, null, Editor.ctrlKey + '+Shift+E'); + this.addAction('selectAll', function() { graph.selectAll(null, true); }, null, null, Editor.ctrlKey + '+A'); + this.addAction('selectNone', function() { graph.clearSelection(); }, null, null, Editor.ctrlKey + '+Shift+A'); + this.addAction('lockUnlock', function() + { + if (!graph.isSelectionEmpty()) + { + graph.getModel().beginUpdate(); + try + { + var defaultValue = graph.isCellMovable(graph.getSelectionCell()) ? 1 : 0; + graph.toggleCellStyles(mxConstants.STYLE_MOVABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_RESIZABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_ROTATABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_DELETABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, defaultValue); + graph.toggleCellStyles('connectable', defaultValue); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+L'); + + // Navigation actions + this.addAction('home', function() { graph.home(); }, null, null, 'Home'); + this.addAction('exitGroup', function() { graph.exitGroup(); }, null, null, Editor.ctrlKey + '+Shift+Home'); + this.addAction('enterGroup', function() { graph.enterGroup(); }, null, null, Editor.ctrlKey + '+Shift+End'); + this.addAction('collapse', function() { graph.foldCells(true); }, null, null, Editor.ctrlKey + '+Home'); + this.addAction('expand', function() { graph.foldCells(false); }, null, null, Editor.ctrlKey + '+End'); + + // Arrange actions + this.addAction('toFront', function() { graph.orderCells(false); }, null, null, Editor.ctrlKey + '+Shift+F'); + this.addAction('toBack', function() { graph.orderCells(true); }, null, null, Editor.ctrlKey + '+Shift+B'); + + this.addAction('group', function() + { + if (graph.getSelectionCount() == 1) + { + graph.setCellStyles('container', '1'); + } + else + { + graph.setSelectionCell(graph.groupCells(null, 0)); + } + }, null, null, Editor.ctrlKey + '+G'); + this.addAction('ungroup', function() + { + if (graph.getSelectionCount() == 1 && graph.getModel().getChildCount(graph.getSelectionCell()) == 0) + { + graph.setCellStyles('container', '0'); + } + else + { + graph.setSelectionCells(graph.ungroupCells()); + } + }, null, null, Editor.ctrlKey + '+Shift+U'); + this.addAction('removeFromGroup', function() { graph.removeCellsFromParent(); }); + // Adds action + this.addAction('edit', function() + { + if (graph.isEnabled()) + { + graph.startEditingAtCell(); + } + }, null, null, 'F2/Enter'); + this.addAction('editData...', function() + { + var cell = graph.getSelectionCell() || graph.getModel().getRoot(); + ui.showDataDialog(cell); + }, null, null, Editor.ctrlKey + '+M'); + this.addAction('settings', function() + { + var cell = graph.getSelectionCell() || graph.getModel().getRoot(); + ui.showSettingsDialog(cell); + }, null, null, Editor.ctrlKey + '+M'); + this.addAction('editTooltip...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var tooltip = ''; + + if (mxUtils.isNode(cell.value)) + { + var tmp = cell.value.getAttribute('tooltip'); + + if (tmp != null) + { + tooltip = tmp; + } + } + + var dlg = new TextareaDialog(ui, mxResources.get('editTooltip') + ':', tooltip, function(newValue) + { + graph.setTooltipForCell(cell, newValue); + }); + ui.showDialog(dlg.container, 320, 200, true, true); + dlg.init(); + } + }, null, null, 'Alt+Shift+T'); + this.addAction('openLink', function() + { + var link = graph.getLinkForCell(graph.getSelectionCell()); + + if (link != null) + { + graph.openLink(link); + } + }); + this.addAction('editLink...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var value = graph.getLinkForCell(cell) || ''; + + ui.showLinkDialog(value, mxResources.get('apply'), function(link) + { + link = mxUtils.trim(link); + graph.setLinkForCell(cell, (link.length > 0) ? link : null); + }); + } + }, null, null, 'Alt+Shift+L'); + this.put('insertImage', new Action(mxResources.get('image') + '...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.clearSelection(); + ui.actions.get('image').funct(); + } + })).isEnabled = isGraphEnabled; + this.put('insertLink', new Action(mxResources.get('link') + '...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + ui.showLinkDialog('', mxResources.get('insert'), function(link, docs) + { + link = mxUtils.trim(link); + + if (link.length > 0) + { + var icon = null; + var title = graph.getLinkTitle(link); + + if (docs != null && docs.length > 0) + { + icon = docs[0].iconUrl; + title = docs[0].name || docs[0].type; + title = title.charAt(0).toUpperCase() + title.substring(1); + + if (title.length > 30) + { + title = title.substring(0, 30) + '...'; + } + } + + var pt = graph.getFreeInsertPoint(); + var linkCell = new mxCell(title, new mxGeometry(pt.x, pt.y, 100, 40), + 'fontColor=#0000EE;fontStyle=4;rounded=1;overflow=hidden;' + ((icon != null) ? + 'shape=label;imageWidth=16;imageHeight=16;spacingLeft=26;align=left;image=' + icon : + 'spacing=10;')); + linkCell.vertex = true; + + graph.setLinkForCell(linkCell, link); + graph.cellSizeUpdated(linkCell, true); + + graph.getModel().beginUpdate(); + try + { + linkCell = graph.addCell(linkCell); + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [linkCell])); + } + finally + { + graph.getModel().endUpdate(); + } + + graph.setSelectionCell(linkCell); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + }); + } + })).isEnabled = isGraphEnabled; + this.addAction('link...', mxUtils.bind(this, function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled()) + { + if (graph.cellEditor.isContentEditing()) + { + var elt = graph.getSelectedElement(); + var link = graph.getParentByName(elt, 'A', graph.cellEditor.textarea); + var oldValue = ''; + + // Workaround for FF returning the outermost selected element after double + // click on a DOM hierarchy with a link inside (but not as topmost element) + if (link == null && elt != null && elt.getElementsByTagName != null) + { + // Finds all links in the selected DOM and uses the link + // where the selection text matches its text content + var links = elt.getElementsByTagName('a'); + + for (var i = 0; i < links.length && link == null; i++) + { + if (links[i].textContent == elt.textContent) + { + link = links[i]; + } + } + } + + if (link != null && link.nodeName == 'A') + { + oldValue = link.getAttribute('href') || ''; + graph.selectNode(link); + } + + var selState = graph.cellEditor.saveSelection(); + + ui.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value) + { + graph.cellEditor.restoreSelection(selState); + + if (value != null) + { + graph.insertLink(value); + } + })); + } + else if (graph.isSelectionEmpty()) + { + this.get('insertLink').funct(); + } + else + { + this.get('editLink').funct(); + } + } + })).isEnabled = isGraphEnabled; + this.addAction('autosize', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().getChildCount(cell)) + { + graph.updateGroupBounds([cell], 20); + } + else + { + var state = graph.view.getState(cell); + var geo = graph.getCellGeometry(cell); + + if (graph.getModel().isVertex(cell) && state != null && state.text != null && + geo != null && graph.isWrapping(cell)) + { + geo = geo.clone(); + geo.height = state.text.boundingBox.height / graph.view.scale; + graph.getModel().setGeometry(cell, geo); + } + else + { + graph.updateCellSize(cell); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+Shift+Y'); + this.addAction('formattedText', function() + { + var refState = graph.getView().getState(graph.getSelectionCell()); + + if (refState != null) + { + graph.stopEditing(); + var value = (refState.style['html'] == '1') ? null : '1'; + + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + state = graph.getView().getState(cells[i]); + + if (state != null) + { + var html = mxUtils.getValue(state.style, 'html', '0'); + + if (html == '1' && value == null) + { + var label = graph.convertValueToString(state.cell); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Removes newlines from HTML and converts breaks to newlines + // to match the HTML output in plain text + label = label.replace(/\n/g, '').replace(//g, '\n'); + } + + // Removes HTML tags + var temp = document.createElement('div'); + temp.innerHTML = label; + label = mxUtils.extractTextWithWhitespace(temp.childNodes); + + graph.cellLabelChanged(state.cell, label); + graph.setCellStyles('html', value, [cells[i]]); + } + else if (html == '0' && value == '1') + { + // Converts HTML tags to text + var label = mxUtils.htmlEntities(graph.convertValueToString(state.cell), false); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Converts newlines in plain text to breaks in HTML + // to match the plain text output + label = label.replace(/\n/g, '
'); + } + + graph.cellLabelChanged(state.cell, graph.sanitizeHtml(label)); + graph.setCellStyles('html', value, [cells[i]]); + } + } + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['html'], + 'values', [(value != null) ? value : '0'], 'cells', cells)); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('wordWrap', function() + { + var state = graph.getView().getState(graph.getSelectionCell()); + var value = 'wrap'; + + graph.stopEditing(); + + if (state != null && state.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') + { + value = null; + } + + graph.setCellStyles(mxConstants.STYLE_WHITE_SPACE, value); + }); + this.addAction('rotation', function() + { + var value = '0'; + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + value = state.style[mxConstants.STYLE_ROTATION] || value; + } + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), function(newValue) + { + if (newValue != null && newValue.length > 0) + { + graph.setCellStyles(mxConstants.STYLE_ROTATION, newValue); + } + }, mxResources.get('enterValue') + ' (' + mxResources.get('rotation') + ' 0-360)'); + + ui.showDialog(dlg.container, 375, 80, true, true); + dlg.init(); + }); + // View actions + this.addAction('resetView', function() + { + graph.zoomTo(1); + ui.resetScrollbars(); + }, null, null, Editor.ctrlKey + '+H'); + this.addAction('zoomIn', function(evt) + { + if (graph.isFastZoomEnabled()) + { + graph.lazyZoom(true, true, ui.buttonZoomDelay); + } + else + { + graph.zoomIn(); + } + }, null, null, Editor.ctrlKey + ' + (Numpad) / Alt+Mousewheel'); + this.addAction('zoomOut', function(evt) + { + if (graph.isFastZoomEnabled()) + { + graph.lazyZoom(false, true, ui.buttonZoomDelay); + } + else + { + graph.zoomOut(); + } + }, null, null, Editor.ctrlKey + ' - (Numpad) / Alt+Mousewheel'); + this.addAction('fitWindow', function() + { + var bounds = (graph.isSelectionEmpty()) ? graph.getGraphBounds() : graph.getBoundingBox(graph.getSelectionCells()); + var t = graph.view.translate; + var s = graph.view.scale; + bounds.width /= s; + bounds.height /= s; + bounds.x = bounds.x / s - t.x; + bounds.y = bounds.y / s - t.y; + + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + var scale = Math.floor(20 * Math.min(cw / bounds.width, ch / bounds.height)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + graph.container.scrollTop = (bounds.y + t.y) * scale - + Math.max((ch - bounds.height * scale) / 2 + 5, 0); + graph.container.scrollLeft = (bounds.x + t.x) * scale - + Math.max((cw - bounds.width * scale) / 2 + 5, 0); + } + }, null, null, Editor.ctrlKey + '+Shift+H'); + this.addAction('fitPage', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + var scale = Math.floor(20 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = pad.y * graph.view.scale - 1; + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, (graph.container.scrollWidth - graph.container.clientWidth) / 2) - 1; + } + }), null, null, Editor.ctrlKey + '+J'); + this.addAction('fitTwoPages', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + + var scale = Math.floor(20 * Math.min(cw / (2 * fmt.width) / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = Math.min(pad.y, (graph.container.scrollHeight - graph.container.clientHeight) / 2); + graph.container.scrollLeft = Math.min(pad.x, (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + }), null, null, Editor.ctrlKey + '+Shift+J'); + this.addAction('fitPageWidth', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + + var scale = Math.floor(20 * cw / fmt.width / ps) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, + (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + })); + this.put('customZoom', new Action(mxResources.get('custom') + '...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.getView().getScale() * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + graph.zoomTo(val / 100); + } + }), mxResources.get('zoom') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }), null, null, Editor.ctrlKey + '+0')); + this.addAction('pageScale...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.pageScale * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + var change = new ChangePageSetup(ui, null, null, null, val / 100); + change.ignoreColor = true; + change.ignoreImage = true; + + graph.model.execute(change); + } + }), mxResources.get('pageScale') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + })); + + // Option actions + var action = null; + action = this.addAction('grid', function() + { + graph.setGridEnabled(!graph.isGridEnabled()); + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, null, null, Editor.ctrlKey + '+Shift+G'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.isGridEnabled(); }); + action.setEnabled(false); + + action = this.addAction('guides', function() + { + graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled; + ui.fireEvent(new mxEventObject('guidesEnabledChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; }); + action.setEnabled(false); + + action = this.addAction('tooltips', function() + { + graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); }); + + action = this.addAction('collapseExpand', function() + { + var change = new ChangePageSetup(ui); + change.ignoreColor = true; + change.ignoreImage = true; + change.foldingEnabled = !graph.foldingEnabled; + + graph.model.execute(change); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.foldingEnabled; }); + action.isEnabled = isGraphEnabled; + action = this.addAction('scrollbars', function() + { + ui.setScrollbars(!ui.hasScrollbars()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.scrollbars; }); + action = this.addAction('pageView', mxUtils.bind(this, function() + { + ui.setPageVisible(!graph.pageVisible); + })); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.pageVisible; }); + action = this.addAction('connectionArrows', function() + { + graph.connectionArrowsEnabled = !graph.connectionArrowsEnabled; + ui.fireEvent(new mxEventObject('connectionArrowsChanged')); + }, null, null, 'Alt+Shift+A'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionArrowsEnabled; }); + action = this.addAction('connectionPoints', function() + { + graph.setConnectable(!graph.connectionHandler.isEnabled()); + ui.fireEvent(new mxEventObject('connectionPointsChanged')); + }, null, null, 'Alt+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); }); + action = this.addAction('copyConnect', function() + { + graph.connectionHandler.setCreateTarget(!graph.connectionHandler.isCreateTarget()); + ui.fireEvent(new mxEventObject('copyConnectChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isCreateTarget(); }); + action.isEnabled = isGraphEnabled; + action = this.addAction('autosave', function() + { + ui.editor.setAutosave(!ui.editor.autosave); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return ui.editor.autosave; }); + action.isEnabled = isGraphEnabled; + action.visible = false; + + // Help actions + this.addAction('help', function() + { + var ext = ''; + + if (mxResources.isLanguageSupported(mxClient.language)) + { + ext = '_' + mxClient.language; + } + + graph.openLink(RESOURCES_PATH + '/help' + ext + '.html'); + }); + + var showingAbout = false; + + this.put('about', new Action(mxResources.get('about') + ' Graph Editor...', function() + { + if (!showingAbout) + { + ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true, function() + { + showingAbout = false; + }); + + showingAbout = true; + } + })); + + // Font style actions + var toggleFontStyle = mxUtils.bind(this, function(key, style, fn, shortcut) + { + return this.addAction(key, function() + { + if (fn != null && graph.cellEditor.isContentEditing()) + { + fn(); + } + else + { + graph.stopEditing(false); + + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style, cells); + + // Removes bold and italic tags and CSS styles inside labels + if ((style & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontWeight = null; + + if (elt.nodeName == 'B') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontStyle = null; + + if (elt.nodeName == 'I') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.textDecoration = null; + + if (elt.nodeName == 'U') + { + graph.replaceElement(elt); + } + }); + } + + for (var i = 0; i < cells.length; i++) + { + if (graph.model.getChildCount(cells[i]) == 0) + { + graph.autoSizeCell(cells[i], false); + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, shortcut); + }); + + toggleFontStyle('bold', mxConstants.FONT_BOLD, function() { document.execCommand('bold', false, null); }, Editor.ctrlKey + '+B'); + toggleFontStyle('italic', mxConstants.FONT_ITALIC, function() { document.execCommand('italic', false, null); }, Editor.ctrlKey + '+I'); + toggleFontStyle('underline', mxConstants.FONT_UNDERLINE, function() { document.execCommand('underline', false, null); }, Editor.ctrlKey + '+U'); + + // Color actions + this.addAction('fontColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR, 'forecolor', '000000'); }); + this.addAction('strokeColor...', function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); }); + this.addAction('fillColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); }); + this.addAction('gradientColor...', function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); }); + this.addAction('backgroundColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'backcolor'); }); + this.addAction('borderColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); }); + + // Format actions + this.addAction('vertical', function() { ui.menus.toggleStyle(mxConstants.STYLE_HORIZONTAL, true); }); + this.addAction('shadow', function() { ui.menus.toggleStyle(mxConstants.STYLE_SHADOW); }); + this.addAction('solid', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, null); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', [null, null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dashed', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dotted', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, '1 4'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', '1 4'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('sharp', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('rounded', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '1'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['1', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('toggleRounded', function() + { + if (!graph.isSelectionEmpty() && graph.isEnabled()) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + var style = graph.getCurrentCellStyle(cells[0]); + var value = (mxUtils.getValue(style, mxConstants.STYLE_ROUNDED, '0') == '1') ? '0' : '1'; + + graph.setCellStyles(mxConstants.STYLE_ROUNDED, value); + graph.setCellStyles(mxConstants.STYLE_CURVED, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', [value, '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('curved', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '1'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '1'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('collapsible', function() + { + var state = graph.view.getState(graph.getSelectionCell()); + var value = '1'; + + if (state != null && graph.getFoldingImage(state) != null) + { + value = '0'; + } + + graph.setCellStyles('collapsible', value); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['collapsible'], + 'values', [value], 'cells', graph.getSelectionCells())); + }); + this.addAction('editStyle...', mxUtils.bind(this, function() + { + var cells = graph.getSelectionCells(); + + if (cells != null && cells.length > 0) + { + var model = graph.getModel(); + + var dlg = new TextareaDialog(this.editorUi, mxResources.get('editStyle') + ':', + model.getStyle(cells[0]) || '', function(newValue) + { + if (newValue != null) + { + graph.setCellStyle(mxUtils.trim(newValue), cells); + } + }, null, null, 400, 220); + this.editorUi.showDialog(dlg.container, 420, 300, true, true); + dlg.init(); + } + }), null, null, Editor.ctrlKey + '+E'); + this.addAction('setAsDefaultStyle', function() + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + ui.setDefaultStyle(graph.getSelectionCell()); + } + }, null, null, Editor.ctrlKey + '+Shift+D'); + this.addAction('clearDefaultStyle', function() + { + if (graph.isEnabled()) + { + ui.clearDefaultStyle(); + } + }, null, null, Editor.ctrlKey + '+Shift+R'); + this.addAction('addWaypoint', function() + { + var cell = graph.getSelectionCell(); + + if (cell != null && graph.getModel().isEdge(cell)) + { + var handler = editor.graph.selectionCellsHandler.getHandler(cell); + + if (handler instanceof mxEdgeHandler) + { + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + + var parent = graph.getModel().getParent(cell); + var pgeo = graph.getCellGeometry(parent); + + while (graph.getModel().isVertex(parent) && pgeo != null) + { + dx += pgeo.x; + dy += pgeo.y; + + parent = graph.getModel().getParent(parent); + pgeo = graph.getCellGeometry(parent); + } + + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + handler.addPointAt(handler.state, x, y); + } + } + }); + this.addAction('removeWaypoint', function() + { + // TODO: Action should run with "this" set to action + var rmWaypointAction = ui.actions.get('removeWaypoint'); + + if (rmWaypointAction.handler != null) + { + // NOTE: Popupevent handled and action updated in Menus.createPopupMenu + rmWaypointAction.handler.removePoint(rmWaypointAction.handler.state, rmWaypointAction.index); + } + }); + this.addAction('clearWaypoints', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + cells = graph.addAllEdges(cells); + + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.points = null; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shift+C'); + action = this.addAction('subscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('subscript', false, null); + } + }), null, null, Editor.ctrlKey + '+,'); + action = this.addAction('superscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('superscript', false, null); + } + }), null, null, Editor.ctrlKey + '+.'); + action = this.addAction('indent', mxUtils.bind(this, function() + { + // NOTE: Alt+Tab for outdent implemented via special code in + // keyHandler.getFunction in EditorUi.js. Ctrl+Tab is reserved. + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('indent', false, null); + } + }), null, null, 'Shift+Tab'); + this.addAction('image...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + var title = mxResources.get('image') + ' (' + mxResources.get('url') + '):'; + var state = graph.getView().getState(graph.getSelectionCell()); + var value = ''; + + if (state != null) + { + value = state.style[mxConstants.STYLE_IMAGE] || value; + } + + var selectionState = graph.cellEditor.saveSelection(); + + ui.showImageDialog(title, value, function(newValue, w, h) + { + // Inserts image into HTML text + if (graph.cellEditor.isContentEditing()) + { + graph.cellEditor.restoreSelection(selectionState); + graph.insertImage(newValue, w, h); + } + else + { + var cells = graph.getSelectionCells(); + + if (newValue != null && (newValue.length > 0 || cells.length > 0)) + { + var select = null; + + graph.getModel().beginUpdate(); + try + { + // Inserts new cell if no cell is selected + if (cells.length == 0) + { + var pt = graph.getFreeInsertPoint(); + cells = [graph.insertVertex(graph.getDefaultParent(), null, '', pt.x, pt.y, w, h, + 'shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;')]; + select = cells; + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); + } + + graph.setCellStyles(mxConstants.STYLE_IMAGE, (newValue.length > 0) ? newValue : null, cells); + + // Sets shape only if not already shape with image (label or image) + var style = graph.getCurrentCellStyle(cells[0]); + + if (style[mxConstants.STYLE_SHAPE] != 'image' && style[mxConstants.STYLE_SHAPE] != 'label') + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, 'image', cells); + } + else if (newValue.length == 0) + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, null, cells); + } + + if (graph.getSelectionCount() == 1) + { + if (w != null && h != null) + { + var cell = cells[0]; + var geo = graph.getModel().getGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.width = w; + geo.height = h; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + if (select != null) + { + graph.setSelectionCells(select); + graph.scrollCellToVisible(select[0]); + } + } + } + }, graph.cellEditor.isContentEditing(), !graph.cellEditor.isContentEditing()); + } + }).isEnabled = isGraphEnabled; + action = this.addAction('layers', mxUtils.bind(this, function() + { + if (this.layersWindow == null) + { + // LATER: Check outline window for initial placement + this.layersWindow = new LayersWindow(ui, document.body.offsetWidth - 280, 120, 220, 196); + this.layersWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('layers')); + + this.layersWindow.init(); + } + else + { + this.layersWindow.window.setVisible(!this.layersWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+L'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.layersWindow != null && this.layersWindow.window.isVisible(); })); + action = this.addAction('formatPanel', mxUtils.bind(this, function() + { + ui.toggleFormatPanel(); + }), null, null, Editor.ctrlKey + '+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return ui.formatWidth > 0; })); + action = this.addAction('outline', mxUtils.bind(this, function() + { + if (this.outlineWindow == null) + { + // LATER: Check layers window for initial placement + this.outlineWindow = new OutlineWindow(ui, document.body.offsetWidth - 260, 100, 180, 180); + this.outlineWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('outline')); + } + else + { + this.outlineWindow.window.setVisible(!this.outlineWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+O'); + + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.outlineWindow != null && this.outlineWindow.window.isVisible(); })); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut) +{ + var title; + + if (key.substring(key.length - 3) == '...') + { + key = key.substring(0, key.length - 3); + title = mxResources.get(key) + '...'; + } + else + { + title = mxResources.get(key); + } + + return this.put(key, new Action(title, funct, enabled, iconCls, shortcut)); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.put = function(name, action) +{ + this.actions[name] = action; + + return action; +}; + +/** + * Returns the action for the given name or null if no such action exists. + */ +Actions.prototype.get = function(name) +{ + return this.actions[name]; +}; + +/** + * Constructs a new action for the given parameters. + */ +function Action(label, funct, enabled, iconCls, shortcut) +{ + mxEventSource.call(this); + this.label = label; + this.funct = this.createFunction(funct); + this.enabled = (enabled != null) ? enabled : true; + this.iconCls = iconCls; + this.shortcut = shortcut; + this.visible = true; +}; + +// Action inherits from mxEventSource +mxUtils.extend(Action, mxEventSource); + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.createFunction = function(funct) +{ + return funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setEnabled = function(value) +{ + if (this.enabled != value) + { + this.enabled = value; + this.fireEvent(new mxEventObject('stateChanged')); + } +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isEnabled = function() +{ + return this.enabled; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setToggleAction = function(value) +{ + this.toggleAction = value; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setSelectedCallback = function(funct) +{ + this.selectedCallback = funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isSelected = function() +{ + return this.selectedCallback(); +}; diff --git a/static/mxgraph/examples/grapheditor/www/js/Dialogs.js b/static/mxgraph/examples/grapheditor/www/js/Dialogs.js new file mode 100644 index 0000000..c6c730e --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/Dialogs.js @@ -0,0 +1,2750 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new open dialog. + */ +var OpenDialog = function() +{ + var iframe = document.createElement('iframe'); + iframe.style.backgroundColor = 'transparent'; + iframe.allowTransparency = 'true'; + iframe.style.borderStyle = 'none'; + iframe.style.borderWidth = '0px'; + iframe.style.overflow = 'hidden'; + iframe.frameBorder = '0'; + + // Adds padding as a workaround for box model in older IE versions + var dx = (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) ? 20 : 0; + + iframe.setAttribute('width', (((Editor.useLocalStorage) ? 640 : 320) + dx) + 'px'); + iframe.setAttribute('height', (((Editor.useLocalStorage) ? 480 : 220) + dx) + 'px'); + iframe.setAttribute('src', OPEN_FORM); + + this.container = iframe; +}; + +/** + * Constructs a new color dialog. + */ +var ColorDialog = function(editorUi, color, apply, cancelFn) +{ + this.editorUi = editorUi; + + var input = document.createElement('input'); + input.style.marginBottom = '10px'; + input.style.width = '216px'; + + // Required for picker to render in IE + if (mxClient.IS_IE) + { + input.style.marginTop = '10px'; + document.body.appendChild(input); + } + + this.init = function() + { + if (!mxClient.IS_TOUCH) + { + input.focus(); + } + }; + + var picker = new jscolor.color(input); + picker.pickerOnfocus = false; + picker.showPicker(); + + var div = document.createElement('div'); + jscolor.picker.box.style.position = 'relative'; + jscolor.picker.box.style.width = '230px'; + jscolor.picker.box.style.height = '100px'; + jscolor.picker.box.style.paddingBottom = '10px'; + div.appendChild(jscolor.picker.box); + + var center = document.createElement('center'); + + function createRecentColorTable() + { + var table = addPresets((ColorDialog.recentColors.length == 0) ? ['FFFFFF'] : + ColorDialog.recentColors, 11, 'FFFFFF', true); + table.style.marginBottom = '8px'; + + return table; + }; + + function addPresets(presets, rowLength, defaultColor, addResetOption) + { + rowLength = (rowLength != null) ? rowLength : 12; + var table = document.createElement('table'); + table.style.borderCollapse = 'collapse'; + table.setAttribute('cellspacing', '0'); + table.style.marginBottom = '20px'; + table.style.cellSpacing = '0px'; + var tbody = document.createElement('tbody'); + table.appendChild(tbody); + + var rows = presets.length / rowLength; + + for (var row = 0; row < rows; row++) + { + var tr = document.createElement('tr'); + + for (var i = 0; i < rowLength; i++) + { + (function(clr) + { + var td = document.createElement('td'); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + + if (clr == null) + { + clr = defaultColor; + } + + if (clr == 'none') + { + td.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')'; + } + else + { + td.style.backgroundColor = '#' + clr; + } + + tr.appendChild(td); + + if (clr != null) + { + td.style.cursor = 'pointer'; + + mxEvent.addListener(td, 'click', function() + { + if (clr == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(clr); + } + }); + } + })(presets[row * rowLength + i]); + } + + tbody.appendChild(tr); + } + + if (addResetOption) + { + var td = document.createElement('td'); + td.setAttribute('title', mxResources.get('reset')); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + td.style.backgroundImage = 'url(\'' + Dialog.prototype.closeImage + '\')'; + td.style.backgroundPosition = 'center center'; + td.style.backgroundRepeat = 'no-repeat'; + td.style.cursor = 'pointer'; + + tr.appendChild(td); + + mxEvent.addListener(td, 'click', function() + { + ColorDialog.resetRecentColors(); + table.parentNode.replaceChild(createRecentColorTable(), table); + }); + } + + center.appendChild(table); + + return table; + }; + + div.appendChild(input); + mxUtils.br(div); + + // Adds recent colors + createRecentColorTable(); + + // Adds presets + var table = addPresets(this.presetColors); + table.style.marginBottom = '8px'; + table = addPresets(this.defaultColors); + table.style.marginBottom = '16px'; + + div.appendChild(center); + + var buttons = document.createElement('div'); + buttons.style.textAlign = 'right'; + buttons.style.whiteSpace = 'nowrap'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + var applyFunction = (apply != null) ? apply : this.createApplyFunction(); + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + var color = input.value; + + // Blocks any non-alphabetic chars in colors + if (/(^#?[a-zA-Z0-9]*$)/.test(color)) + { + if (color != 'none' && color.charAt(0) != '#') + { + color = '#' + color; + } + + ColorDialog.addRecentColor((color != 'none') ? color.substring(1) : color, 12); + applyFunction(color); + editorUi.hideDialog(); + } + else + { + editorUi.handleError({message: mxResources.get('invalidInput')}); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + buttons.appendChild(applyBtn); + + if (!editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + if (color != null) + { + if (color == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(color); + } + } + + div.appendChild(buttons); + this.picker = picker; + this.colorInput = input; + + // LATER: Only fires if input if focused, should always + // fire if this dialog is showing. + mxEvent.addListener(div, 'keydown', function(e) + { + if (e.keyCode == 27) + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + + mxEvent.consume(e); + } + }); + + this.container = div; +}; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.presetColors = ['E6D0DE', 'CDA2BE', 'B5739D', 'E1D5E7', 'C3ABD0', 'A680B8', 'D4E1F5', 'A9C4EB', '7EA6E0', 'D5E8D4', '9AC7BF', '67AB9F', 'D5E8D4', 'B9E0A5', '97D077', 'FFF2CC', 'FFE599', 'FFD966', 'FFF4C3', 'FFCE9F', 'FFB570', 'F8CECC', 'F19C99', 'EA6B66']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.defaultColors = ['none', 'FFFFFF', 'E6E6E6', 'CCCCCC', 'B3B3B3', '999999', '808080', '666666', '4D4D4D', '333333', '1A1A1A', '000000', 'FFCCCC', 'FFE6CC', 'FFFFCC', 'E6FFCC', 'CCFFCC', 'CCFFE6', 'CCFFFF', 'CCE5FF', 'CCCCFF', 'E5CCFF', 'FFCCFF', 'FFCCE6', + 'FF9999', 'FFCC99', 'FFFF99', 'CCFF99', '99FF99', '99FFCC', '99FFFF', '99CCFF', '9999FF', 'CC99FF', 'FF99FF', 'FF99CC', 'FF6666', 'FFB366', 'FFFF66', 'B3FF66', '66FF66', '66FFB3', '66FFFF', '66B2FF', '6666FF', 'B266FF', 'FF66FF', 'FF66B3', 'FF3333', 'FF9933', 'FFFF33', + '99FF33', '33FF33', '33FF99', '33FFFF', '3399FF', '3333FF', '9933FF', 'FF33FF', 'FF3399', 'FF0000', 'FF8000', 'FFFF00', '80FF00', '00FF00', '00FF80', '00FFFF', '007FFF', '0000FF', '7F00FF', 'FF00FF', 'FF0080', 'CC0000', 'CC6600', 'CCCC00', '66CC00', '00CC00', '00CC66', + '00CCCC', '0066CC', '0000CC', '6600CC', 'CC00CC', 'CC0066', '990000', '994C00', '999900', '4D9900', '009900', '00994D', '009999', '004C99', '000099', '4C0099', '990099', '99004D', '660000', '663300', '666600', '336600', '006600', '006633', '006666', '003366', '000066', + '330066', '660066', '660033', '330000', '331A00', '333300', '1A3300', '003300', '00331A', '003333', '001933', '000033', '190033', '330033', '33001A']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.createApplyFunction = function() +{ + return mxUtils.bind(this, function(color) + { + var graph = this.editorUi.editor.graph; + + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(this.currentColorKey, color); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [this.currentColorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); +}; + +/** + * + */ +ColorDialog.recentColors = []; + +/** + * Adds recent color for later use. + */ +ColorDialog.addRecentColor = function(color, max) +{ + if (color != null) + { + mxUtils.remove(color, ColorDialog.recentColors); + ColorDialog.recentColors.splice(0, 0, color); + + if (ColorDialog.recentColors.length >= max) + { + ColorDialog.recentColors.pop(); + } + } +}; + +/** + * Adds recent color for later use. + */ +ColorDialog.resetRecentColors = function() +{ + ColorDialog.recentColors = []; +}; + +/** + * Constructs a new about dialog. + */ +var AboutDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.setAttribute('align', 'center'); + var h3 = document.createElement('h3'); + mxUtils.write(h3, mxResources.get('about') + ' GraphEditor'); + div.appendChild(h3); + var img = document.createElement('img'); + img.style.border = '0px'; + img.setAttribute('width', '176'); + img.setAttribute('width', '151'); + img.setAttribute('src', IMAGE_PATH + '/logo.png'); + div.appendChild(img); + mxUtils.br(div); + mxUtils.write(div, 'Powered by mxGraph ' + mxClient.VERSION); + mxUtils.br(div); + var link = document.createElement('a'); + link.setAttribute('href', 'http://www.jgraph.com/'); + link.setAttribute('target', '_blank'); + mxUtils.write(link, 'www.jgraph.com'); + div.appendChild(link); + mxUtils.br(div); + mxUtils.br(div); + var closeBtn = mxUtils.button(mxResources.get('close'), function() + { + editorUi.hideDialog(); + }); + closeBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(closeBtn); + + this.container = div; +}; + +/** + * Constructs a new textarea dialog. + */ +var TextareaDialog = function(editorUi, title, url, fn, cancelFn, cancelTitle, w, h, + addButtons, noHide, noWrap, applyTitle, helpLink, customButtons) +{ + w = (w != null) ? w : 300; + h = (h != null) ? h : 120; + noHide = (noHide != null) ? noHide : false; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, title); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + + var nameInput = document.createElement('textarea'); + + if (noWrap) + { + nameInput.setAttribute('wrap', 'off'); + } + + nameInput.setAttribute('spellcheck', 'false'); + nameInput.setAttribute('autocorrect', 'off'); + nameInput.setAttribute('autocomplete', 'off'); + nameInput.setAttribute('autocapitalize', 'off'); + + mxUtils.write(nameInput, url || ''); + nameInput.style.resize = 'none'; + nameInput.style.width = w + 'px'; + nameInput.style.height = h + 'px'; + + this.textarea = nameInput; + + this.init = function() + { + nameInput.focus(); + nameInput.scrollTop = 0; + }; + + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.style.paddingTop = '14px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + if (helpLink != null) + { + var helpBtn = mxUtils.button(mxResources.get('help'), function() + { + editorUi.editor.graph.openLink(helpLink); + }); + helpBtn.className = 'geBtn'; + + td.appendChild(helpBtn); + } + + if (customButtons != null) + { + for (var i = 0; i < customButtons.length; i++) + { + (function(label, fn) + { + var customBtn = mxUtils.button(label, function(e) + { + fn(e, nameInput); + }); + customBtn.className = 'geBtn'; + + td.appendChild(customBtn); + })(customButtons[i][0], customButtons[i][1]); + } + } + + var cancelBtn = mxUtils.button(cancelTitle || mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (addButtons != null) + { + addButtons(td, nameInput); + } + + if (fn != null) + { + var genericBtn = mxUtils.button(applyTitle || mxResources.get('apply'), function() + { + if (!noHide) + { + editorUi.hideDialog(); + } + + fn(nameInput.value); + }); + + genericBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(genericBtn); + } + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Constructs a new edit file dialog. + */ +var EditDiagramDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.style.textAlign = 'right'; + var textarea = document.createElement('textarea'); + textarea.setAttribute('wrap', 'off'); + textarea.setAttribute('spellcheck', 'false'); + textarea.setAttribute('autocorrect', 'off'); + textarea.setAttribute('autocomplete', 'off'); + textarea.setAttribute('autocapitalize', 'off'); + textarea.style.overflow = 'auto'; + textarea.style.resize = 'none'; + textarea.style.width = '600px'; + textarea.style.height = '360px'; + textarea.style.marginBottom = '16px'; + + textarea.value = mxUtils.getPrettyXml(editorUi.editor.getGraphXml()); + div.appendChild(textarea); + + this.init = function() + { + textarea.focus(); + }; + + // Enables dropping files + if (Graph.fileSupport) + { + function handleDrop(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + + if (evt.dataTransfer.files.length > 0) + { + var file = evt.dataTransfer.files[0]; + var reader = new FileReader(); + + reader.onload = function(e) + { + textarea.value = e.target.result; + }; + + reader.readAsText(file); + } + else + { + textarea.value = editorUi.extractGraphModelFromEvent(evt); + } + }; + + function handleDragOver(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }; + + // Setup the dnd listeners. + textarea.addEventListener('dragover', handleDragOver, false); + textarea.addEventListener('drop', handleDrop, false); + } + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + var select = document.createElement('select'); + select.style.width = '180px'; + select.className = 'geBtn'; + + if (editorUi.editor.graph.isEnabled()) + { + var replaceOption = document.createElement('option'); + replaceOption.setAttribute('value', 'replace'); + mxUtils.write(replaceOption, mxResources.get('replaceExistingDrawing')); + select.appendChild(replaceOption); + } + + var newOption = document.createElement('option'); + newOption.setAttribute('value', 'new'); + mxUtils.write(newOption, mxResources.get('openInNewWindow')); + + if (EditDiagramDialog.showNewWindowOption) + { + select.appendChild(newOption); + } + + if (editorUi.editor.graph.isEnabled()) + { + var importOption = document.createElement('option'); + importOption.setAttribute('value', 'import'); + mxUtils.write(importOption, mxResources.get('addToExistingDrawing')); + select.appendChild(importOption); + } + + div.appendChild(select); + + var okBtn = mxUtils.button(mxResources.get('ok'), function() + { + // Removes all illegal control characters before parsing + var data = Graph.zapGremlins(mxUtils.trim(textarea.value)); + var error = null; + + if (select.value == 'new') + { + editorUi.hideDialog(); + editorUi.editor.editAsNew(data); + } + else if (select.value == 'replace') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + editorUi.editor.setGraphXml(mxUtils.parseXml(data).documentElement); + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + else if (select.value == 'import') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + var doc = mxUtils.parseXml(data); + var model = new mxGraphModel(); + var codec = new mxCodec(doc); + codec.decode(doc.documentElement, model); + + var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); + editorUi.editor.graph.setSelectionCells(editorUi.editor.graph.importCells(children)); + + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + + if (error != null) + { + mxUtils.alert(error.message); + } + }); + okBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(okBtn); + + if (!editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + this.container = div; +}; + +/** + * + */ +EditDiagramDialog.showNewWindowOption = true; + +/** + * Constructs a new export dialog. + */ +var ExportDialog = function(editorUi) +{ + var graph = editorUi.editor.graph; + var bounds = graph.getGraphBounds(); + var scale = graph.view.scale; + + var width = Math.ceil(bounds.width / scale); + var height = Math.ceil(bounds.height / scale); + + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.setAttribute('cellpadding', (mxClient.IS_SF) ? '0' : '2'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, mxResources.get('filename') + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', editorUi.editor.getOrCreateFilename()); + nameInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('format') + ':'); + + row.appendChild(td); + + var imageFormatSelect = document.createElement('select'); + imageFormatSelect.style.width = '180px'; + + var pngOption = document.createElement('option'); + pngOption.setAttribute('value', 'png'); + mxUtils.write(pngOption, mxResources.get('formatPng')); + imageFormatSelect.appendChild(pngOption); + + var gifOption = document.createElement('option'); + + if (ExportDialog.showGifOption) + { + gifOption.setAttribute('value', 'gif'); + mxUtils.write(gifOption, mxResources.get('formatGif')); + imageFormatSelect.appendChild(gifOption); + } + + var jpgOption = document.createElement('option'); + jpgOption.setAttribute('value', 'jpg'); + mxUtils.write(jpgOption, mxResources.get('formatJpg')); + imageFormatSelect.appendChild(jpgOption); + + var pdfOption = document.createElement('option'); + pdfOption.setAttribute('value', 'pdf'); + mxUtils.write(pdfOption, mxResources.get('formatPdf')); + imageFormatSelect.appendChild(pdfOption); + + var svgOption = document.createElement('option'); + svgOption.setAttribute('value', 'svg'); + mxUtils.write(svgOption, mxResources.get('formatSvg')); + imageFormatSelect.appendChild(svgOption); + + if (ExportDialog.showXmlOption) + { + var xmlOption = document.createElement('option'); + xmlOption.setAttribute('value', 'xml'); + mxUtils.write(xmlOption, mxResources.get('formatXml')); + imageFormatSelect.appendChild(xmlOption); + } + + td = document.createElement('td'); + td.appendChild(imageFormatSelect); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('zoom') + ' (%):'); + + row.appendChild(td); + + var zoomInput = document.createElement('input'); + zoomInput.setAttribute('type', 'number'); + zoomInput.setAttribute('value', '100'); + zoomInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(zoomInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('width') + ':'); + + row.appendChild(td); + + var widthInput = document.createElement('input'); + widthInput.setAttribute('value', width); + widthInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(widthInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('height') + ':'); + + row.appendChild(td); + + var heightInput = document.createElement('input'); + heightInput.setAttribute('value', height); + heightInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(heightInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('dpi') + ':'); + + row.appendChild(td); + + var dpiSelect = document.createElement('select'); + dpiSelect.style.width = '180px'; + + var dpi100Option = document.createElement('option'); + dpi100Option.setAttribute('value', '100'); + mxUtils.write(dpi100Option, '100dpi'); + dpiSelect.appendChild(dpi100Option); + + var dpi200Option = document.createElement('option'); + dpi200Option.setAttribute('value', '200'); + mxUtils.write(dpi200Option, '200dpi'); + dpiSelect.appendChild(dpi200Option); + + var dpi300Option = document.createElement('option'); + dpi300Option.setAttribute('value', '300'); + mxUtils.write(dpi300Option, '300dpi'); + dpiSelect.appendChild(dpi300Option); + + var dpi400Option = document.createElement('option'); + dpi400Option.setAttribute('value', '400'); + mxUtils.write(dpi400Option, '400dpi'); + dpiSelect.appendChild(dpi400Option); + + var dpiCustOption = document.createElement('option'); + dpiCustOption.setAttribute('value', 'custom'); + mxUtils.write(dpiCustOption, mxResources.get('custom')); + dpiSelect.appendChild(dpiCustOption); + + var customDpi = document.createElement('input'); + customDpi.style.width = '180px'; + customDpi.style.display = 'none'; + customDpi.setAttribute('value', '100'); + customDpi.setAttribute('type', 'number'); + customDpi.setAttribute('min', '50'); + customDpi.setAttribute('step', '50'); + + var zoomUserChanged = false; + + mxEvent.addListener(dpiSelect, 'change', function() + { + if (this.value == 'custom') + { + this.style.display = 'none'; + customDpi.style.display = ''; + customDpi.focus(); + } + else + { + customDpi.value = this.value; + + if (!zoomUserChanged) + { + zoomInput.value = this.value; + } + } + }); + + mxEvent.addListener(customDpi, 'change', function() + { + var dpi = parseInt(customDpi.value); + + if (isNaN(dpi) || dpi <= 0) + { + customDpi.style.backgroundColor = 'red'; + } + else + { + customDpi.style.backgroundColor = ''; + + if (!zoomUserChanged) + { + zoomInput.value = dpi; + } + } + }); + + td = document.createElement('td'); + td.appendChild(dpiSelect); + td.appendChild(customDpi); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('background') + ':'); + + row.appendChild(td); + + var transparentCheckbox = document.createElement('input'); + transparentCheckbox.setAttribute('type', 'checkbox'); + transparentCheckbox.checked = graph.background == null || graph.background == mxConstants.NONE; + + td = document.createElement('td'); + td.appendChild(transparentCheckbox); + mxUtils.write(td, mxResources.get('transparent')); + + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('borderWidth') + ':'); + + row.appendChild(td); + + var borderInput = document.createElement('input'); + borderInput.setAttribute('type', 'number'); + borderInput.setAttribute('value', ExportDialog.lastBorderValue); + borderInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(borderInput); + row.appendChild(td); + + tbody.appendChild(row); + table.appendChild(tbody); + + // Handles changes in the export format + function formatChanged() + { + var name = nameInput.value; + var dot = name.lastIndexOf('.'); + + if (dot > 0) + { + nameInput.value = name.substring(0, dot + 1) + imageFormatSelect.value; + } + else + { + nameInput.value = name + '.' + imageFormatSelect.value; + } + + if (imageFormatSelect.value === 'xml') + { + zoomInput.setAttribute('disabled', 'true'); + widthInput.setAttribute('disabled', 'true'); + heightInput.setAttribute('disabled', 'true'); + borderInput.setAttribute('disabled', 'true'); + } + else + { + zoomInput.removeAttribute('disabled'); + widthInput.removeAttribute('disabled'); + heightInput.removeAttribute('disabled'); + borderInput.removeAttribute('disabled'); + } + + if (imageFormatSelect.value === 'png' || imageFormatSelect.value === 'svg') + { + transparentCheckbox.removeAttribute('disabled'); + } + else + { + transparentCheckbox.setAttribute('disabled', 'disabled'); + } + + if (imageFormatSelect.value === 'png') + { + dpiSelect.removeAttribute('disabled'); + customDpi.removeAttribute('disabled'); + } + else + { + dpiSelect.setAttribute('disabled', 'disabled'); + customDpi.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(imageFormatSelect, 'change', formatChanged); + formatChanged(); + + function checkValues() + { + if (widthInput.value * heightInput.value > MAX_AREA || widthInput.value <= 0) + { + widthInput.style.backgroundColor = 'red'; + } + else + { + widthInput.style.backgroundColor = ''; + } + + if (widthInput.value * heightInput.value > MAX_AREA || heightInput.value <= 0) + { + heightInput.style.backgroundColor = 'red'; + } + else + { + heightInput.style.backgroundColor = ''; + } + }; + + mxEvent.addListener(zoomInput, 'change', function() + { + zoomUserChanged = true; + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + zoomInput.value = parseFloat((s * 100).toFixed(2)); + + if (width > 0) + { + widthInput.value = Math.floor(width * s); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(widthInput, 'change', function() + { + var s = parseInt(widthInput.value) / width; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(heightInput, 'change', function() + { + var s = parseInt(heightInput.value) / height; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + widthInput.value = Math.floor(width * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.setAttribute('align', 'right'); + td.style.paddingTop = '22px'; + td.colSpan = 2; + + var saveBtn = mxUtils.button(mxResources.get('export'), mxUtils.bind(this, function() + { + if (parseInt(zoomInput.value) <= 0) + { + mxUtils.alert(mxResources.get('drawingEmpty')); + } + else + { + var name = nameInput.value; + var format = imageFormatSelect.value; + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + var b = Math.max(0, parseInt(borderInput.value)); + var bg = graph.background; + var dpi = Math.max(1, parseInt(customDpi.value)); + + if ((format == 'svg' || format == 'png') && transparentCheckbox.checked) + { + bg = null; + } + else if (bg == null || bg == mxConstants.NONE) + { + bg = '#ffffff'; + } + + ExportDialog.lastBorderValue = b; + ExportDialog.exportFile(editorUi, name, format, bg, s, b, dpi); + } + })); + saveBtn.className = 'geBtn gePrimaryBtn'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + td.appendChild(saveBtn); + } + else + { + td.appendChild(saveBtn); + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Remembers last value for border. + */ +ExportDialog.lastBorderValue = 0; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showGifOption = true; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showXmlOption = true; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.exportFile = function(editorUi, name, format, bg, s, b, dpi) +{ + var graph = editorUi.editor.graph; + + if (format == 'xml') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(editorUi.editor.getGraphXml()), name, format); + } + else if (format == 'svg') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(graph.getSvg(bg, s, b)), name, format); + } + else + { + var bounds = graph.getGraphBounds(); + + // New image export + var xmlDoc = mxUtils.createXmlDocument(); + var root = xmlDoc.createElement('output'); + xmlDoc.appendChild(root); + + // Renders graph. Offset will be multiplied with state's scale when painting state. + var xmlCanvas = new mxXmlCanvas2D(root); + xmlCanvas.translate(Math.floor((b / s - bounds.x) / graph.view.scale), + Math.floor((b / s - bounds.y) / graph.view.scale)); + xmlCanvas.scale(s / graph.view.scale); + + var imgExport = new mxImageExport() + imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); + + // Puts request data together + var param = 'xml=' + encodeURIComponent(mxUtils.getXml(root)); + var w = Math.ceil(bounds.width * s / graph.view.scale + 2 * b); + var h = Math.ceil(bounds.height * s / graph.view.scale + 2 * b); + + // Requests image if request is valid + if (param.length <= MAX_REQUEST_SIZE && w * h < MAX_AREA) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(EXPORT_URL, 'format=' + format + + '&filename=' + encodeURIComponent(name) + + '&bg=' + ((bg != null) ? bg : 'none') + + '&w=' + w + '&h=' + h + '&' + param + + '&dpi=' + dpi); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + } + } +}; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.saveLocalFile = function(editorUi, data, filename, format) +{ + if (data.length < MAX_REQUEST_SIZE) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(SAVE_URL, 'xml=' + encodeURIComponent(data) + '&filename=' + + encodeURIComponent(filename) + '&format=' + format); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + mxUtils.popup(xml); + } +}; + +/** + * Constructs a new metadata dialog. + */ +var EditDataDialog = function(ui, cell) +{ + var div = document.createElement('div'); + var graph = ui.editor.graph; + + var value = graph.getModel().getValue(cell); + + // Converts the value to an XML node + if (!mxUtils.isNode(value)) + { + var doc = mxUtils.createXmlDocument(); + var obj = doc.createElement('object'); + obj.setAttribute('label', value || ''); + value = obj; + } + + // Creates the dialog contents + var form = new mxForm('properties'); + form.table.style.width = '100%'; + + var attrs = value.attributes; + var names = []; + var texts = []; + var count = 0; + + var id = (EditDataDialog.getDisplayIdForCell != null) ? + EditDataDialog.getDisplayIdForCell(ui, cell) : null; + + var addRemoveButton = function(text, name) + { + var wrapper = document.createElement('div'); + wrapper.style.position = 'relative'; + wrapper.style.paddingRight = '20px'; + wrapper.style.boxSizing = 'border-box'; + wrapper.style.width = '100%'; + + var removeAttr = document.createElement('a'); + var img = mxUtils.createImage(Dialog.prototype.closeImage); + img.style.height = '9px'; + img.style.fontSize = '9px'; + img.style.marginBottom = (mxClient.IS_IE11) ? '-1px' : '5px'; + + removeAttr.className = 'geButton'; + removeAttr.setAttribute('title', mxResources.get('delete')); + removeAttr.style.position = 'absolute'; + removeAttr.style.top = '4px'; + removeAttr.style.right = '0px'; + removeAttr.style.margin = '0px'; + removeAttr.style.width = '9px'; + removeAttr.style.height = '9px'; + removeAttr.style.cursor = 'pointer'; + removeAttr.appendChild(img); + + var removeAttrFn = (function(name) + { + return function() + { + var count = 0; + + for (var j = 0; j < names.length; j++) + { + if (names[j] == name) + { + texts[j] = null; + form.table.deleteRow(count + ((id != null) ? 1 : 0)); + + break; + } + + if (texts[j] != null) + { + count++; + } + } + }; + })(name); + + mxEvent.addListener(removeAttr, 'click', removeAttrFn); + + var parent = text.parentNode; + wrapper.appendChild(text); + wrapper.appendChild(removeAttr); + parent.appendChild(wrapper); + }; + + var addTextArea = function(index, name, value) + { + names[index] = name; + texts[index] = form.addTextarea(names[count] + ':', value, 2); + texts[index].style.width = '100%'; + + if (value.indexOf('\n') > 0) + { + texts[index].setAttribute('rows', '2'); + } + + addRemoveButton(texts[index], name); + }; + + var temp = []; + var isLayer = graph.getModel().getParent(cell) == graph.getModel().getRoot(); + + for (var i = 0; i < attrs.length; i++) + { + if ((isLayer || attrs[i].nodeName != 'label') && attrs[i].nodeName != 'placeholders') + { + temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); + } + } + + // Sorts by name + temp.sort(function(a, b) + { + if (a.name < b.name) + { + return -1; + } + else if (a.name > b.name) + { + return 1; + } + else + { + return 0; + } + }); + + if (id != null) + { + var text = document.createElement('div'); + text.style.width = '100%'; + text.style.fontSize = '11px'; + text.style.textAlign = 'center'; + mxUtils.write(text, id); + + form.addField(mxResources.get('id') + ':', text); + } + + for (var i = 0; i < temp.length; i++) + { + addTextArea(count, temp[i].name, temp[i].value); + count++; + } + + var top = document.createElement('div'); + top.style.cssText = 'position:absolute;left:30px;right:30px;overflow-y:auto;top:30px;bottom:80px;'; + top.appendChild(form.table); + + var newProp = document.createElement('div'); + newProp.style.boxSizing = 'border-box'; + newProp.style.paddingRight = '160px'; + newProp.style.whiteSpace = 'nowrap'; + newProp.style.marginTop = '6px'; + newProp.style.width = '100%'; + + var nameInput = document.createElement('input'); + nameInput.setAttribute('placeholder', mxResources.get('enterPropertyName')); + nameInput.setAttribute('type', 'text'); + nameInput.setAttribute('size', (mxClient.IS_IE || mxClient.IS_IE11) ? '36' : '40'); + nameInput.style.boxSizing = 'border-box'; + nameInput.style.marginLeft = '2px'; + nameInput.style.width = '100%'; + + newProp.appendChild(nameInput); + top.appendChild(newProp); + div.appendChild(top); + + var addBtn = mxUtils.button(mxResources.get('addProperty'), function() + { + var name = nameInput.value; + + // Avoid ':' in attribute names which seems to be valid in Chrome + if (name.length > 0 && name != 'label' && name != 'placeholders' && name.indexOf(':') < 0) + { + try + { + var idx = mxUtils.indexOf(names, name); + + if (idx >= 0 && texts[idx] != null) + { + texts[idx].focus(); + } + else + { + // Checks if the name is valid + var clone = value.cloneNode(false); + clone.setAttribute(name, ''); + + if (idx >= 0) + { + names.splice(idx, 1); + texts.splice(idx, 1); + } + + names.push(name); + var text = form.addTextarea(name + ':', '', 2); + text.style.width = '100%'; + texts.push(text); + addRemoveButton(text, name); + + text.focus(); + } + + addBtn.setAttribute('disabled', 'disabled'); + nameInput.value = ''; + } + catch (e) + { + mxUtils.alert(e); + } + } + else + { + mxUtils.alert(mxResources.get('invalidName')); + } + }); + + this.init = function() + { + if (texts.length > 0) + { + texts[0].focus(); + } + else + { + nameInput.focus(); + } + }; + + addBtn.setAttribute('title', mxResources.get('addProperty')); + addBtn.setAttribute('disabled', 'disabled'); + addBtn.style.textOverflow = 'ellipsis'; + addBtn.style.position = 'absolute'; + addBtn.style.overflow = 'hidden'; + addBtn.style.width = '144px'; + addBtn.style.right = '0px'; + addBtn.className = 'geBtn'; + newProp.appendChild(addBtn); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + ui.hideDialog.apply(ui, arguments); + }); + + cancelBtn.className = 'geBtn'; + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + try + { + ui.hideDialog.apply(ui, arguments); + + // Clones and updates the value + value = value.cloneNode(true); + var removeLabel = false; + + for (var i = 0; i < names.length; i++) + { + if (texts[i] == null) + { + value.removeAttribute(names[i]); + } + else + { + value.setAttribute(names[i], texts[i].value); + removeLabel = removeLabel || (names[i] == 'placeholder' && + value.getAttribute('placeholders') == '1'); + } + } + + // Removes label if placeholder is assigned + if (removeLabel) + { + value.removeAttribute('label'); + } + + // Updates the value of the cell (undoable) + graph.getModel().setValue(cell, value); + } + catch (e) + { + mxUtils.alert(e); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + + function updateAddBtn() + { + if (nameInput.value.length > 0) + { + addBtn.removeAttribute('disabled'); + } + else + { + addBtn.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(nameInput, 'keyup', updateAddBtn); + + // Catches all changes that don't fire a keyup (such as paste via mouse) + mxEvent.addListener(nameInput, 'change', updateAddBtn); + + var buttons = document.createElement('div'); + buttons.style.cssText = 'position:absolute;left:30px;right:30px;text-align:right;bottom:30px;height:40px;' + + if (ui.editor.graph.getModel().isVertex(cell) || ui.editor.graph.getModel().isEdge(cell)) + { + var replace = document.createElement('span'); + replace.style.marginRight = '10px'; + var input = document.createElement('input'); + input.setAttribute('type', 'checkbox'); + input.style.marginRight = '6px'; + + if (value.getAttribute('placeholders') == '1') + { + input.setAttribute('checked', 'checked'); + input.defaultChecked = true; + } + + mxEvent.addListener(input, 'click', function() + { + if (value.getAttribute('placeholders') == '1') + { + value.removeAttribute('placeholders'); + } + else + { + value.setAttribute('placeholders', '1'); + } + }); + + replace.appendChild(input); + mxUtils.write(replace, mxResources.get('placeholders')); + + if (EditDataDialog.placeholderHelpLink != null) + { + var link = document.createElement('a'); + link.setAttribute('href', EditDataDialog.placeholderHelpLink); + link.setAttribute('title', mxResources.get('help')); + link.setAttribute('target', '_blank'); + link.style.marginLeft = '8px'; + link.style.cursor = 'help'; + + var icon = document.createElement('img'); + mxUtils.setOpacity(icon, 50); + icon.style.height = '16px'; + icon.style.width = '16px'; + icon.setAttribute('border', '0'); + icon.setAttribute('valign', 'middle'); + icon.style.marginTop = (mxClient.IS_IE11) ? '0px' : '-4px'; + icon.setAttribute('src', Editor.helpImage); + link.appendChild(icon); + + replace.appendChild(link); + } + + buttons.appendChild(replace); + } + + if (ui.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + buttons.appendChild(applyBtn); + } + else + { + buttons.appendChild(applyBtn); + buttons.appendChild(cancelBtn); + } + + div.appendChild(buttons); + this.container = div; +}; + +/** + * Constructs a new settings dialog. + */ + var EditSettingsDialog = function(ui, cell) + { + var div = document.createElement('div'); + var graph = ui.editor.graph; + + var values = graph.getModel().getValue(cell); + + // Converts the value to an XML node + if (!mxUtils.isNode(values)) + { + var doc = mxUtils.createXmlDocument(); + var obj = doc.createElement('object'); + obj.setAttribute('label', values || ''); + values = obj; + } + + + // Creates the dialog contents + var form = new mxForm('properties'); + form.table.style.width = '100%'; + + + + var id = (EditSettingsDialog.getDisplayIdForCell != null) ? + EditSettingsDialog.getDisplayIdForCell(ui, cell) : null; + + if (id != null) + { + var text = document.createElement('div'); + text.style.width = '100%'; + text.style.fontSize = '11px'; + text.style.textAlign = 'center'; + mxUtils.write(text, id); + + var name + Object.values(OC_WORKSPACE[cell.rType]).forEach(key => { + console.log(key) + if (key.ID==cell.rID) { + name = key.name + + } + + }); + + form.addField(cell.rType +': ' + name, text); + + } + + var top = document.createElement('div'); + var hr = document.createElement("hr"); + hr.style.color = 'grey' + top.appendChild(form.table); + top.appendChild(hr) + + var coreLabel = document.createTextNode(mxResources.get('core')); + var coreInput = document.createElement('input'); + coreInput.setAttribute('type', 'text'); + coreInput.style.boxSizing = 'border-box'; + coreInput.style.marginLeft = '2px'; + coreInput.style.width = '20%'; + + top.appendChild(coreLabel); + top.appendChild(coreInput); + div.appendChild(top); + + var ramLabel = document.createTextNode(mxResources.get('ram')); + var ramInput = document.createElement('input'); + ramInput.setAttribute('type', 'text'); + ramInput.style.boxSizing = 'border-box'; + ramInput.style.marginLeft = '2px'; + ramInput.style.width = '20%'; + + top.appendChild(ramLabel); + top.appendChild(ramInput); + div.appendChild(top); + + var durationLabel = document.createTextNode(mxResources.get('estDuration')); + var durationInput = document.createElement('input'); + durationInput.setAttribute('type', 'text'); + durationInput.placeholder = 'in seconds' + durationInput.style.boxSizing = 'border-box'; + durationInput.style.marginLeft = '2px'; + durationInput.style.width = '20%'; + top.appendChild(durationLabel); + top.appendChild(durationInput); + + var commandLabel = document.createTextNode(mxResources.get('command')); + var commandInput = document.createElement('input'); + commandInput.setAttribute('type', 'text'); + commandInput.style.boxSizing = 'border-box'; + commandInput.style.marginLeft = '2px'; + commandInput.style.width = '100%'; + top.appendChild(commandLabel); + top.appendChild(commandInput); + + var argsLabel = document.createTextNode(mxResources.get('args')); + var argsInput = document.createElement('input'); + argsInput.setAttribute('type', 'text'); + argsInput.placeholder = '"arg1","arg2",...' + argsInput.style.boxSizing = 'border-box'; + argsInput.style.marginLeft = '2px'; + argsInput.style.width = '100%'; + top.appendChild(argsLabel); + top.appendChild(argsInput); + + var envLabel = document.createTextNode(mxResources.get('environment')); + var envInput = document.createElement('input'); + envInput.setAttribute('type', 'text'); + envInput.placeholder = '"env1"="value1","env2"="value2",...' + envInput.style.boxSizing = 'border-box'; + envInput.style.marginLeft = '2px'; + envInput.style.width = '100%'; + top.appendChild(envLabel); + top.appendChild(envInput); + + var portsLabel = document.createTextNode(mxResources.get('ports')); + var portsInput = document.createElement('input'); + portsInput.setAttribute('type', 'text'); + portsInput.placeholder = '8080,8443,...' + portsInput.style.boxSizing = 'border-box'; + portsInput.style.marginLeft = '2px'; + portsInput.style.width = '100%'; + top.appendChild(portsLabel); + top.appendChild(portsInput); + + + div.appendChild(top); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + ui.hideDialog.apply(ui, arguments); + }); + + cancelBtn.className = 'geBtn'; + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + ui.hideDialog.apply(ui, arguments); + + // Clones and updates the value + values = values.cloneNode(true); + + values.setAttribute('core', coreInput.value) + + values.setAttribute('ram', ramInput.value) + + values.setAttribute('duration', durationInput.value) + values.setAttribute('command', commandInput.value) + values.setAttribute('args', argsInput.value) + values.setAttribute('env', envInput.value) + values.setAttribute('ports', portsInput.value) + graph.getModel().setValue(cell, values); + console.log(cell) + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + + var buttons = document.createElement('div'); + buttons.style.cssText = 'position:absolute;left:30px;right:30px;text-align:right;bottom:30px;height:40px;' + + if (ui.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + buttons.appendChild(applyBtn); + } + else + { + buttons.appendChild(applyBtn); + buttons.appendChild(cancelBtn); + } + + div.appendChild(buttons); + this.container = div; + }; + + /** + * Optional help link. + */ + EditSettingsDialog.getDisplayIdForCell = function(ui, cell) + { + var id = null; + + if (ui.editor.graph.getModel().getParent(cell) != null) + { + id = cell.getId(); + } + + return id; + }; + + /** + * Optional help link. + */ + EditSettingsDialog.placeholderHelpLink = null; + +/** + * Optional help link. + */ +EditDataDialog.getDisplayIdForCell = function(ui, cell) +{ + var id = null; + + if (ui.editor.graph.getModel().getParent(cell) != null) + { + id = cell.getId(); + } + + return id; +}; + +/** + * Optional help link. + */ +EditDataDialog.placeholderHelpLink = null; + +/** + * Constructs a new link dialog. + */ +var LinkDialog = function(editorUi, initialValue, btnLabel, fn) +{ + var div = document.createElement('div'); + mxUtils.write(div, mxResources.get('editLink') + ':'); + + var inner = document.createElement('div'); + inner.className = 'geTitle'; + inner.style.backgroundColor = 'transparent'; + inner.style.borderColor = 'transparent'; + inner.style.whiteSpace = 'nowrap'; + inner.style.textOverflow = 'clip'; + inner.style.cursor = 'default'; + + if (!mxClient.IS_VML) + { + inner.style.paddingRight = '20px'; + } + + var linkInput = document.createElement('input'); + linkInput.setAttribute('value', initialValue); + linkInput.setAttribute('placeholder', 'http://www.example.com/'); + linkInput.setAttribute('type', 'text'); + linkInput.style.marginTop = '6px'; + linkInput.style.width = '400px'; + linkInput.style.backgroundImage = 'url(\'' + Dialog.prototype.clearImage + '\')'; + linkInput.style.backgroundRepeat = 'no-repeat'; + linkInput.style.backgroundPosition = '100% 50%'; + linkInput.style.paddingRight = '14px'; + + var cross = document.createElement('div'); + cross.setAttribute('title', mxResources.get('reset')); + cross.style.position = 'relative'; + cross.style.left = '-16px'; + cross.style.width = '12px'; + cross.style.height = '14px'; + cross.style.cursor = 'pointer'; + + // Workaround for inline-block not supported in IE + cross.style.display = (mxClient.IS_VML) ? 'inline' : 'inline-block'; + cross.style.top = ((mxClient.IS_VML) ? 0 : 3) + 'px'; + + // Needed to block event transparency in IE + cross.style.background = 'url(' + IMAGE_PATH + '/transparent.gif)'; + + mxEvent.addListener(cross, 'click', function() + { + linkInput.value = ''; + linkInput.focus(); + }); + + inner.appendChild(linkInput); + inner.appendChild(cross); + div.appendChild(inner); + + this.init = function() + { + linkInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) + { + linkInput.select(); + } + else + { + document.execCommand('selectAll', false, null); + } + }; + + var btns = document.createElement('div'); + btns.style.marginTop = '18px'; + btns.style.textAlign = 'right'; + + mxEvent.addListener(linkInput, 'keypress', function(e) + { + if (e.keyCode == 13) + { + editorUi.hideDialog(); + fn(linkInput.value); + } + }); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + var mainBtn = mxUtils.button(btnLabel, function() + { + editorUi.hideDialog(); + fn(linkInput.value); + }); + mainBtn.className = 'geBtn gePrimaryBtn'; + btns.appendChild(mainBtn); + + if (!editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + div.appendChild(btns); + + this.container = div; +}; + +/** + * + */ +var OutlineWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.width = '100%'; + div.style.height = '100%'; + div.style.border = '1px solid whiteSmoke'; + div.style.overflow = 'hidden'; + + this.window = new mxWindow(mxResources.get('outline'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 80, 80); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth; + var ih = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + var outline = editorUi.createOutline(this.window); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + outline.destroy(); + } + + this.window.addListener(mxEvent.RESIZE, mxUtils.bind(this, function() + { + outline.update(false); + outline.outline.sizeDidChange(); + })); + + this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function() + { + this.window.fit(); + outline.suspended = false; + outline.outline.refresh(); + outline.update(); + })); + + this.window.addListener(mxEvent.HIDE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + this.window.addListener(mxEvent.NORMALIZE, mxUtils.bind(this, function() + { + outline.suspended = false; + outline.update(); + })); + + this.window.addListener(mxEvent.MINIMIZE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + var outlineCreateGraph = outline.createGraph; + outline.createGraph = function(container) + { + var g = outlineCreateGraph.apply(this, arguments); + g.gridEnabled = false; + g.pageScale = graph.pageScale; + g.pageFormat = graph.pageFormat; + g.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; + g.pageVisible = graph.pageVisible; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + return g; + }; + + function update() + { + outline.outline.pageScale = graph.pageScale; + outline.outline.pageFormat = graph.pageFormat; + outline.outline.pageVisible = graph.pageVisible; + outline.outline.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background;; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + if (graph.view.backgroundPageShape != null && outline.outline.view.backgroundPageShape != null) + { + outline.outline.view.backgroundPageShape.fill = graph.view.backgroundPageShape.fill; + } + + outline.outline.refresh(); + }; + + outline.init(div); + + editorUi.editor.addListener('resetGraphView', update); + editorUi.addListener('pageFormatChanged', update); + editorUi.addListener('backgroundColorChanged', update); + editorUi.addListener('backgroundImageChanged', update); + editorUi.addListener('pageViewChanged', function() + { + update(); + outline.update(true); + }); + + if (outline.outline.dialect == mxConstants.DIALECT_SVG) + { + var zoomInAction = editorUi.actions.get('zoomIn'); + var zoomOutAction = editorUi.actions.get('zoomOut'); + + mxEvent.addMouseWheelListener(function(evt, up) + { + var outlineWheel = false; + var source = mxEvent.getSource(evt); + + while (source != null) + { + if (source == outline.outline.view.canvas.ownerSVGElement) + { + outlineWheel = true; + break; + } + + source = source.parentNode; + } + + if (outlineWheel) + { + if (up) + { + zoomInAction.funct(); + } + else + { + zoomOutAction.funct(); + } + } + }); + } +}; + +/** + * + */ +var LayersWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.userSelect = 'none'; + div.style.background = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + div.style.border = '1px solid whiteSmoke'; + div.style.height = '100%'; + div.style.marginBottom = '10px'; + div.style.overflow = 'auto'; + + var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px'; + + var listDiv = document.createElement('div') + listDiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? '#dcdcdc' : Dialog.backdropColor; + listDiv.style.position = 'absolute'; + listDiv.style.overflow = 'auto'; + listDiv.style.left = '0px'; + listDiv.style.right = '0px'; + listDiv.style.top = '0px'; + listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px'; + div.appendChild(listDiv); + + var dragSource = null; + var dropIndex = null; + + mxEvent.addListener(div, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = 0; + evt.stopPropagation(); + evt.preventDefault(); + }); + + // Workaround for "no element found" error in FF + mxEvent.addListener(div, 'drop', function(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }); + + var layerCount = null; + var selectionLayer = null; + var ldiv = document.createElement('div'); + + ldiv.className = 'geToolbarContainer'; + ldiv.style.position = 'absolute'; + ldiv.style.bottom = '0px'; + ldiv.style.left = '0px'; + ldiv.style.right = '0px'; + ldiv.style.height = tbarHeight; + ldiv.style.overflow = 'hidden'; + ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px'; + ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + ldiv.style.borderWidth = '1px 0px 0px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.display = 'block'; + ldiv.style.whiteSpace = 'nowrap'; + + if (mxClient.IS_QUIRKS) + { + ldiv.style.filter = 'none'; + } + + var link = document.createElement('a'); + link.className = 'geButton'; + + if (mxClient.IS_QUIRKS) + { + link.style.filter = 'none'; + } + + var removeLink = link.cloneNode(); + removeLink.innerHTML = '
'; + + mxEvent.addListener(removeLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + try + { + var index = graph.model.root.getIndex(selectionLayer); + graph.removeCells([selectionLayer], false); + + // Creates default layer if no layer exists + if (graph.model.getChildCount(graph.model.root) == 0) + { + graph.model.add(graph.model.root, new mxCell()); + graph.setDefaultParent(null); + } + else if (index > 0 && index <= graph.model.getChildCount(graph.model.root)) + { + graph.setDefaultParent(graph.model.getChildAt(graph.model.root, index - 1)); + } + else + { + graph.setDefaultParent(null); + } + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + removeLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(removeLink); + + var insertLink = link.cloneNode(); + insertLink.setAttribute('title', mxUtils.trim(mxResources.get('moveSelectionTo', ['']))); + insertLink.innerHTML = '
'; + + mxEvent.addListener(insertLink, 'click', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + editorUi.editor.graph.popupMenuHandler.hideMenu(); + + var menu = new mxPopupMenu(mxUtils.bind(this, function(menu, parent) + { + for (var i = layerCount - 1; i >= 0; i--) + { + (mxUtils.bind(this, function(child) + { + var item = menu.addItem(graph.convertValueToString(child) || + mxResources.get('background'), null, mxUtils.bind(this, function() + { + graph.moveCells(graph.getSelectionCells(), 0, 0, false, child); + }), parent); + + if (graph.getSelectionCount() == 1 && graph.model.isAncestor(child, graph.getSelectionCell())) + { + menu.addCheckmark(item, Editor.checkmarkImage); + } + + }))(graph.model.getChildAt(graph.model.root, i)); + } + })); + menu.div.className += ' geMenubarMenu'; + menu.smartSeparators = true; + menu.showDisabled = true; + menu.autoExpand = true; + + // Disables autoexpand and destroys menu when hidden + menu.hideMenu = mxUtils.bind(this, function() + { + mxPopupMenu.prototype.hideMenu.apply(menu, arguments); + menu.destroy(); + }); + + var offset = mxUtils.getOffset(insertLink); + menu.popup(offset.x, offset.y + insertLink.offsetHeight, null, evt); + + // Allows hiding by clicking on document + editorUi.setCurrentMenu(menu); + } + }); + + ldiv.appendChild(insertLink); + + var dataLink = link.cloneNode(); + dataLink.innerHTML = '
'; + dataLink.setAttribute('title', mxResources.get('rename')); + + mxEvent.addListener(dataLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + editorUi.showDataDialog(selectionLayer); + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + dataLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(dataLink); + + function renameLayer(layer) + { + if (graph.isEnabled() && layer != null) + { + var label = graph.convertValueToString(layer); + var dlg = new FilenameDialog(editorUi, label || mxResources.get('background'), mxResources.get('rename'), mxUtils.bind(this, function(newValue) + { + if (newValue != null) + { + graph.cellLabelChanged(layer, newValue); + } + }), mxResources.get('enterName')); + editorUi.showDialog(dlg.container, 300, 100, true, true); + dlg.init(); + } + }; + + var duplicateLink = link.cloneNode(); + duplicateLink.innerHTML = '
'; + + mxEvent.addListener(duplicateLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + var newCell = null; + graph.model.beginUpdate(); + try + { + newCell = graph.cloneCell(selectionLayer); + graph.cellLabelChanged(newCell, mxResources.get('untitledLayer')); + newCell.setVisible(true); + newCell = graph.addCell(newCell, graph.model.root); + graph.setDefaultParent(newCell); + } + finally + { + graph.model.endUpdate(); + } + + if (newCell != null && !graph.isCellLocked(newCell)) + { + graph.selectAll(newCell); + } + } + }); + + if (!graph.isEnabled()) + { + duplicateLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(duplicateLink); + + var addLink = link.cloneNode(); + addLink.innerHTML = '
'; + addLink.setAttribute('title', mxResources.get('addLayer')); + + mxEvent.addListener(addLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + + try + { + var cell = graph.addCell(new mxCell(mxResources.get('untitledLayer')), graph.model.root); + graph.setDefaultParent(cell); + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + addLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(addLink); + div.appendChild(ldiv); + + function refresh() + { + layerCount = graph.model.getChildCount(graph.model.root) + listDiv.innerHTML = ''; + + function addLayer(index, label, child, defaultParent) + { + var ldiv = document.createElement('div'); + ldiv.className = 'geToolbarContainer'; + + ldiv.style.overflow = 'hidden'; + ldiv.style.position = 'relative'; + ldiv.style.padding = '4px'; + ldiv.style.height = '22px'; + ldiv.style.display = 'block'; + ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + ldiv.style.borderWidth = '0px 0px 1px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.whiteSpace = 'nowrap'; + ldiv.setAttribute('title', label); + + var left = document.createElement('div'); + left.style.display = 'inline-block'; + left.style.width = '100%'; + left.style.textOverflow = 'ellipsis'; + left.style.overflow = 'hidden'; + + mxEvent.addListener(ldiv, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = index; + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(ldiv, 'dragstart', function(evt) + { + dragSource = ldiv; + + // Workaround for no DnD on DIV in FF + if (mxClient.IS_FF) + { + // LATER: Check what triggers a parse as XML on this in FF after drop + evt.dataTransfer.setData('Text', ''); + } + }); + + mxEvent.addListener(ldiv, 'dragend', function(evt) + { + if (dragSource != null && dropIndex != null) + { + graph.addCell(child, graph.model.root, dropIndex); + } + + dragSource = null; + dropIndex = null; + evt.stopPropagation(); + evt.preventDefault(); + }); + + var btn = document.createElement('img'); + btn.setAttribute('draggable', 'false'); + btn.setAttribute('align', 'top'); + btn.setAttribute('border', '0'); + btn.style.padding = '4px'; + btn.setAttribute('title', mxResources.get('lockUnlock')); + + var style = graph.getCurrentCellStyle(child); + + if (mxUtils.getValue(style, 'locked', '0') == '1') + { + btn.setAttribute('src', Dialog.prototype.lockedImage); + } + else + { + btn.setAttribute('src', Dialog.prototype.unlockedImage); + } + + if (graph.isEnabled()) + { + btn.style.cursor = 'pointer'; + } + + mxEvent.addListener(btn, 'click', function(evt) + { + if (graph.isEnabled()) + { + var value = null; + + graph.getModel().beginUpdate(); + try + { + value = (mxUtils.getValue(style, 'locked', '0') == '1') ? null : '1'; + graph.setCellStyles('locked', value, [child]); + } + finally + { + graph.getModel().endUpdate(); + } + + if (value == '1') + { + graph.removeSelectionCells(graph.getModel().getDescendants(child)); + } + + mxEvent.consume(evt); + } + }); + + left.appendChild(btn); + + var inp = document.createElement('input'); + inp.setAttribute('type', 'checkbox'); + inp.setAttribute('title', mxResources.get('hideIt', [child.value || mxResources.get('background')])); + inp.style.marginLeft = '4px'; + inp.style.marginRight = '6px'; + inp.style.marginTop = '4px'; + left.appendChild(inp); + + if (graph.model.isVisible(child)) + { + inp.setAttribute('checked', 'checked'); + inp.defaultChecked = true; + } + + mxEvent.addListener(inp, 'click', function(evt) + { + graph.model.setVisible(child, !graph.model.isVisible(child)); + mxEvent.consume(evt); + }); + + mxUtils.write(left, label); + ldiv.appendChild(left); + + if (graph.isEnabled()) + { + // Fallback if no drag and drop is available + if (mxClient.IS_TOUCH || mxClient.IS_POINTER || mxClient.IS_VML || + (mxClient.IS_IE && document.documentMode < 10)) + { + var right = document.createElement('div'); + right.style.display = 'block'; + right.style.textAlign = 'right'; + right.style.whiteSpace = 'nowrap'; + right.style.position = 'absolute'; + right.style.right = '6px'; + right.style.top = '6px'; + + // Poor man's change layer order + if (index > 0) + { + var img2 = document.createElement('a'); + + img2.setAttribute('title', mxResources.get('toBack')); + + img2.className = 'geButton'; + img2.style.cssFloat = 'none'; + img2.innerHTML = '▼'; + img2.style.width = '14px'; + img2.style.height = '14px'; + img2.style.fontSize = '14px'; + img2.style.margin = '0px'; + img2.style.marginTop = '-1px'; + right.appendChild(img2); + + mxEvent.addListener(img2, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index - 1); + } + + mxEvent.consume(evt); + }); + } + + if (index >= 0 && index < layerCount - 1) + { + var img1 = document.createElement('a'); + + img1.setAttribute('title', mxResources.get('toFront')); + + img1.className = 'geButton'; + img1.style.cssFloat = 'none'; + img1.innerHTML = '▲'; + img1.style.width = '14px'; + img1.style.height = '14px'; + img1.style.fontSize = '14px'; + img1.style.margin = '0px'; + img1.style.marginTop = '-1px'; + right.appendChild(img1); + + mxEvent.addListener(img1, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index + 1); + } + + mxEvent.consume(evt); + }); + } + + ldiv.appendChild(right); + } + + if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10)) + { + ldiv.setAttribute('draggable', 'true'); + ldiv.style.cursor = 'move'; + } + } + + mxEvent.addListener(ldiv, 'dblclick', function(evt) + { + var nodeName = mxEvent.getSource(evt).nodeName; + + if (nodeName != 'INPUT' && nodeName != 'IMG') + { + renameLayer(child); + mxEvent.consume(evt); + } + }); + + if (graph.getDefaultParent() == child) + { + ldiv.style.background = (Dialog.backdropColor == 'white') ? '#e6eff8' : '#505759'; + ldiv.style.fontWeight = (graph.isEnabled()) ? 'bold' : ''; + selectionLayer = child; + } + else + { + mxEvent.addListener(ldiv, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.setDefaultParent(defaultParent); + graph.view.setCurrentRoot(null); + refresh(); + } + }); + } + + listDiv.appendChild(ldiv); + }; + + // Cannot be moved or deleted + for (var i = layerCount - 1; i >= 0; i--) + { + (mxUtils.bind(this, function(child) + { + addLayer(i, graph.convertValueToString(child) || + mxResources.get('background'), child, child); + }))(graph.model.getChildAt(graph.model.root, i)); + } + + var label = graph.convertValueToString(selectionLayer) || mxResources.get('background'); + removeLink.setAttribute('title', mxResources.get('removeIt', [label])); + duplicateLink.setAttribute('title', mxResources.get('duplicateIt', [label])); + dataLink.setAttribute('title', mxResources.get('editData')); + + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + }; + + refresh(); + graph.model.addListener(mxEvent.CHANGE, function() + { + refresh(); + }); + + graph.selectionModel.addListener(mxEvent.CHANGE, function() + { + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + else + { + insertLink.className = 'geButton'; + } + }); + + this.window = new mxWindow(mxResources.get('layers'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 120, 120); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + this.init = function() + { + listDiv.scrollTop = listDiv.scrollHeight - listDiv.clientHeight; + }; + + this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function() + { + this.window.fit(); + })); + + // Make refresh available via instance + this.refreshLayers = refresh; + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth; + var ih = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + } +}; diff --git a/static/mxgraph/examples/grapheditor/www/js/Editor.js b/static/mxgraph/examples/grapheditor/www/js/Editor.js new file mode 100644 index 0000000..c3ecb05 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/Editor.js @@ -0,0 +1,2898 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Editor constructor executed on page load. + */ +Editor = function (chromeless, themes, model, graph, editable) { + mxEventSource.call(this); + this.chromeless = (chromeless != null) ? chromeless : this.chromeless; + this.initStencilRegistry(); + this.graph = graph || this.createGraph(themes, model); + this.editable = (editable != null) ? editable : !chromeless; + this.undoManager = this.createUndoManager(); + this.status = ''; + + this.getOrCreateFilename = function () { + return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml'; + }; + + this.getFilename = function () { + return this.filename; + }; + + // Sets the status and fires a statusChanged event + this.setStatus = function (value) { + this.status = value; + this.fireEvent(new mxEventObject('statusChanged')); + }; + + // Returns the current status + this.getStatus = function () { + return this.status; + }; + + // Updates modified state if graph changes + this.graphChangeListener = function (sender, eventObject) { + var edit = (eventObject != null) ? eventObject.getProperty('edit') : null; + + if (edit == null || !edit.ignoreEdit) { + this.setModified(true); + } + + OC_CATALOG_API.then(client => { + client.apis + .workflow + .WorkflowController_Parse_mxGraph({ + workflowName: OC_PROJECT_NAME, + xmlData: mxUtils.getPrettyXml(MX_EDITORUI.editor.getGraphXml()) + }) + .then(answer => { + // if (answer.status == 201) { + MX_EDITORUI.editor.errorlogs_array = answer.obj + + targetWindow = $("#errorlogs_list")[0] + if (targetWindow) { + MX_EDITORUI.UPDATE_LOGS(targetWindow) + } + // } + console.log(response) + + }) + .catch((error) => { + console.log("Workspace parse mxgraphxml: " + error) + }); + }); + }; + + this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function () { + this.graphChangeListener.apply(this, arguments); + })); + + // Sets persistent graph state defaults + this.graph.resetViewOnRootChange = false; + this.init(); +}; + +/** + * Counts open editor tabs (must be global for cross-window access) + */ +Editor.pageCounter = 0; + +// Cross-domain window access is not allowed in FF, so if we +// were opened from another domain then this will fail. +(function () { + try { + var op = window; + + while (op.opener != null && typeof op.opener.Editor !== 'undefined' && + !isNaN(op.opener.Editor.pageCounter) && + // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795 + op.opener != op) { + op = op.opener; + } + + // Increments the counter in the first opener in the chain + if (op != null) { + op.Editor.pageCounter++; + Editor.pageCounter = op.Editor.pageCounter; + } + } + catch (e) { + // ignore + } +})(); + +/** + * Specifies if local storage should be used (eg. on the iPad which has no filesystem) + */ +Editor.useLocalStorage = typeof (Storage) != 'undefined' && mxClient.IS_IOS; + +/** + * + */ +Editor.moveImage = (mxClient.IS_SVG) ? 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI4cHgiIGhlaWdodD0iMjhweCI+PGc+PC9nPjxnPjxnPjxnPjxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIuNCwyLjQpc2NhbGUoMC44KXJvdGF0ZSg0NSwxMiwxMikiIHN0cm9rZT0iIzI5YjZmMiIgZmlsbD0iIzI5YjZmMiIgZD0iTTE1LDNsMi4zLDIuM2wtMi44OSwyLjg3bDEuNDIsMS40MkwxOC43LDYuN0wyMSw5VjNIMTV6IE0zLDlsMi4zLTIuM2wyLjg3LDIuODlsMS40Mi0xLjQyTDYuNyw1LjNMOSwzSDNWOXogTTksMjEgbC0yLjMtMi4zbDIuODktMi44N2wtMS40Mi0xLjQyTDUuMywxNy4zTDMsMTV2Nkg5eiBNMjEsMTVsLTIuMywyLjNsLTIuODctMi44OWwtMS40MiwxLjQybDIuODksMi44N0wxNSwyMWg2VjE1eiIvPjwvZz48L2c+PC9nPjwvc3ZnPgo=' : + IMAGE_PATH + '/move.png'; + +/** + * Images below are for lightbox and embedding toolbars. + */ +Editor.helpImage = (mxClient.IS_SVG) ? 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJub25lIiBkPSJNMCAwaDI0djI0SDB6Ii8+PHBhdGggZD0iTTExIDE4aDJ2LTJoLTJ2MnptMS0xNkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDh6bTAtMTRjLTIuMjEgMC00IDEuNzktNCA0aDJjMC0xLjEuOS0yIDItMnMyIC45IDIgMmMwIDItMyAxLjc1LTMgNWgyYzAtMi4yNSAzLTIuNSAzLTUgMC0yLjIxLTEuNzktNC00LTR6Ii8+PC9zdmc+' : + IMAGE_PATH + '/help.png'; + +/** + * Sets the default font size. + */ +Editor.checkmarkImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhFQAVAMQfAGxsbHx8fIqKioaGhvb29nJycvr6+sDAwJqamltbW5OTk+np6YGBgeTk5Ly8vJiYmP39/fLy8qWlpa6ursjIyOLi4vj4+N/f3+3t7fT09LCwsHZ2dubm5r6+vmZmZv///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEY4NTZERTQ5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEY4NTZERTU5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Rjg1NkRFMjlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Rjg1NkRFMzlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAB8ALAAAAAAVABUAAAVI4CeOZGmeaKqubKtylktSgCOLRyLd3+QJEJnh4VHcMoOfYQXQLBcBD4PA6ngGlIInEHEhPOANRkaIFhq8SuHCE1Hb8Lh8LgsBADs=' : + IMAGE_PATH + '/checkmark.gif'; + +/** + * Images below are for lightbox and embedding toolbars. + */ +Editor.maximizeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABXRSTlMA758vX1Pw3BoAAABJSURBVAjXY8AJQkODGBhUQ0MhbAUGBiYY24CBgRnGFmZgMISwgwwDGRhEhVVBbAVmEQYGRwMmBjIAQi/CTIRd6G5AuA3dzYQBAHj0EFdHkvV4AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomOutImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCxxcXEhISFgYGChjTUxAAAAAXRSTlMAQObYZgAAAEdJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITgLJZXFxgbIQ4Qj3CHIT5ggoIe5kgNkM1KSDYKBKqxPkDAPo5BAZBE54hAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomInImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCwhISFxcXFgYGBavKaoAAAAAXRSTlMAQObYZgAAAElJREFUCNdjIAMwCQrB2YKCggJQJqMIA4sglK3owMzgqABVwsDMwCgAZTMbG8PYCHGEeoQ5CPMFFRD2MkFshmpSQLBRJFSJ8wcAEqcEM2uhl2MAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomFitImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAD1BMVEUAAAAAAAAwMDBwcHBgYGC1xl09AAAAAXRSTlMAQObYZgAAAEFJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITwMdGqEeYgzBfUAFhLxPEZqgmBQQbRUKFOH8AAK5OA3lA+FFOAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.layersImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAAaVBMVEUAAAAgICAICAgdHR0PDw8WFhYICAgLCwsXFxcvLy8ODg4uLi4iIiIqKiokJCQYGBgKCgonJycFBQUCAgIqKiocHBwcHBwODg4eHh4cHBwnJycJCQkUFBQqKiojIyMuLi4ZGRkgICAEBATOWYXAAAAAGnRSTlMAD7+fnz8/H7/ff18/77+vr5+fn39/b28fH2xSoKsAAACQSURBVBjTrYxJEsMgDARZZMAY73sgCcn/HxnhKtnk7j6oRq0psfuoyndZ/SuODkHPLzfVT6KeyPePnJ7KrnkRjWMXTn4SMnN8mXe2SSM3ts8L/ZUxxrbAULSYJJULE0Iw9pjpenoICcgcX61mGgTgtCv9Be99pzCoDhNQWQnchD1mup5++CYGcoQexajZbfwAj/0MD8ZOaUgAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.previousImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAh0lEQVQ4je3UsQnCUBCA4U8hpa1NsoEjpHQJS0dxADdwEMuMIJkgA1hYChbGQgMi+JC8q4L/AB/vDu7x74cWWEZhJU44RmA1zujR5GIbXF9YNrjD/Q0bDRY4fEBZ4P4LlgTnCbAf84pUM8/9hY08tMUtEoQ1LpEgrNBFglChFXR6Q6GfwwR6AGKJMF74Vtt3AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.nextImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAi0lEQVQ4jeXUIQ7CUAwA0MeGxWI2yylwnALJUdBcgYvM7QYLmjOQIAkIPmJZghiIvypoUtX0tfnJL38X5ZfaEgUeUcManFBHgS0SLlhHggk3bCPBhCf2keCQR8wjwYTDp6YiZxJmOU1jGw7vGALescuBxsArNlOwd/CM1VSM/ut1qCIw+uOwiMJ+OF4CQzBCXm3hyAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.editImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCwALAIABAFdXV////yH5BAEAAAEALAAAAAALAAsAAAIZjB8AiKuc4jvLOGqzrjX6zmkWyChXaUJBAQA7' : IMAGE_PATH + '/edit.gif'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomOutLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABJklEQVQ4y4WT2XaDMAxEvWD2nSSUNEnTJN3r//+9Sj7ILAY6L0ijC4ONYVZRpo6cByrz2YKSUGorGTpz71lPVHvT+avoB5wIkU/mxk8veceSuNoLg44IzziXjvpih72wKQnm8yc2UoiP/LAd8jQfe2Xf4Pq+2EyYIvv9wbzHHCgwxDdlBtWZOdqDfTCVgqpygQpsZaojVAVc9UjQxnAJDIBhiQv84tq3gMQCAVTxVoSibXJf8tMuc7e1TB/DCmejBNg/w1Y3c+AM5vv4w7xM59/oXamrHaLVqPQ+OTCnmMZxgz0SdL5zji0/ld6j88qGa5KIiBB6WeJGKfUKwSMKLuXgvl1TW0tm5R9UQL/efSDYsnzxD8CinhBsTTdugJatKpJwf8v+ADb8QmvW7AeAAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomInLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABKElEQVQ4y4WT6WKCMBCENwkBwn2oFKvWqr3L+79es4EkQIDOH2d3Pxk2ABiJlB8JCXjqw4LikHVGLHTm3nM3UeVN5690GBBN0GwyV/3kkrUQR+WeKnREeKpzaXWd77CmJiXGfPIEI4V4yQ9TIW/ntlcMBe731Vts9w5TWG8F5j3mQI4hvrKpdGeYA7CX9qAcl650gVJartxRuhyHVghF8idQAIbFLvCLu28BsQEC6aKtCK6Pyb3JT7PmbmtNH8Ny56CotD/2qOs5cJbuffxgXmCib+xddVU5RNOhkvvkhTlFehzVWCOh3++MYElOhfdovaImnRYVmqDdsuhNp1QrBBE6uGC2+3ZNjGdg5B94oD+9uyVgWT79BwAxEBTWdOu3bWBVgsn/N/AHUD9IC01Oe40AAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.actualSizeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABIUlEQVQ4y4WT2XqDIBCFBxDc9yTWNEnTJN3r+79eGT4BEbXnaubMr8dBBaM450dCQp4LWFAascGIRd48eB4cNYE7f6XjgGiCFs5c+dml6CFN6j1V6IQIlHPpdV/usKcmJcV88gQTRXjLD9Mhb+fWq8YG9/uCmTCFjeeDeY85UGKIUGUuqzN42kv7oCouq9oHamlzVR1lVfpAIu1QVRiW+sAv7r4FpAYIZZVsRXB9TP5Dfpo1d1trCgzz1iiptH/sUbdz4CzN9+mLeXHn3+hdddd4RDegsrvzwZwSs2GLPRJidAqCLTlVwaMPqpYMWjTWBB2WRW86pVkhSKyDK2bdt2tmagZG4sBD/evdLQHLEvQfAOKRoLCmG1FAB6uKmby+gz+REDn7O5+EwQAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.printLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAXVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9RKvvlAAAAHnRSTlMAydnl77qbMLT093H7K4Nd4Ktn082+lYt5bkklEgP44nQSAAAApUlEQVQ4y73P2Q6DIBRF0cOgbRHHzhP//5m9mBAQKjG1cT0Yc7ITAMu1LNQgUZiQ2DYoNQ0sCQb6qgHAfRx48opq3J9AZ6xuF7uOew8Ik1OsCZRS2UAC9V+D9a+QZYxNA45YFQftPtSkATOhw7dAc0vPBwKWiIOjP0JZ0yMuQJ27g36DipOUsqRAM0dR8KD1/ILHaHSE/w8DIx09E3g/BTce6rHUB5sAPKvfF+JdAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.layersLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAmVBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v7///+bnZkkAAAAMnRSTlMABPr8ByiD88KsTi/rvJb272mjeUA1CuPe1M/KjVxYHxMP6KZ0S9nYzGRGGRaznpGIbzaGUf0AAAHESURBVDjLbZLZYoIwEEVDgLCjbKIgAlqXqt3m/z+uNwu1rcyDhjl3ktnYL7OY254C0VX3yWFZfzDrOClbbgKxi0YDHjwl4jbnRkXxJS/C1YP3DbBhD1n7Ex4uaAqdVDb3yJ/4J/3nJD2to/ngQz/DfUvzMp4JJ5sSCaF5oXmemgQDfDxzbi+Kq4sU+vNcuAmx94JtyOP2DD4Epz2asWSCz4Z/4fECxyNj9zC9xNLHcdPEO+awDKeSaUu0W4twZQiO2hYVisTR3RCtK/c1X6t4xMEpiGqXqVntEBLolkZZsKY4QtwH6jzq67dEHlJysB1aNOD3XT7n1UkasQN59L4yC2RELMDSeCRtz3yV22Ub3ozIUTknYx8JWqDdQxbUes98cR2kZtUSveF/bAhcedwEWmlxIkpZUy4XOCb6VBjjxHvbwo/1lBAHHi2JCr0NI570QhyHq/DhJoE2lLgyA4RVe6KmZ47O/3b86MCP0HWa73A8/C3SUc5Qc1ajt6fgpXJ+RGpMvDSchepZDOOQRcZVIKcK90x2D7etqtI+56+u6n3sPriO6nfphitR4+O2m3EbM7lh3me1FM1o+LMI887rN+s3/wZdTFlpNVJiOAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.closeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAuvAIg/dDM/QlOeuFhj0S5s4vKgzjxJRQNiLSey0AAADNSURBVDjLfZLbEoMgDEQjRRRs1XqX///QNmOHJSnjPkHOGR7IEmeoGtJZstnwjqbRfIsmgEdtPCqe9Ynz7ZSc07rE2QiSc+qv8TvjRXA2PDUm3dpe82iJhOEUfxJJo3aCv+jKmRmH4lcCjCjeh9GWOdL/GZZkXH3PYYDrHBnfc4D/RVZf5sjoC1was+Y6HQxwaUxFvq/a0Pv343VCTxfBSRiB+ab3M3eiQZXmMNBJ3Y8pGRZtYQ7DgHMXJEdPLTaN/qBjzJOBc3nmNcbsA16bMR0oLqf+AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.editLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA+hzi3nRQWyXzkm0h2j3u54gzEgSXjlYoTBgJxL2loGpAOS3Jt7Wxm35Ga7gRAAAA6UlEQVQ4y63Q2XaCMBSF4Q0JBasoQ5DJqbXjfv8HbCK2BZNwo/8FXHx7rcMC7lQu0iX8qU/qtvAWCpoqH8dYzS0SwaV5eK/UAf8X9pd2CWKzuF5Jrftp1owXwnIGLUaL3PYndOHf4kNNXWrXK/m7CHunk7K8LE6YtBpcknwG9GKxnroY+ylBXcx4xKyx/u/EuXi509cP9V7OO1oyHnzrdFTcqLG/4ibBA5pIMr/4xvKzuQDkVy9wW8SgBFD6HDvuzMvrZcC9QlkfMzI7w64m+b4PqBMNHB05lH21PVxJo2/fBXxV4hB38PcD+5AkI4FuETsAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.previousLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFZJREFUOMvdkskRgDAMA4lDwg2B7b9XOlge/KKvdsa25KFb5XlRvxXC/DNBEv8IFNjBgGdDgXtFgTyhwDXiQAUHCvwa4Uv6mR6UR+1led2mVonvl+tML45qCQNQLIx7AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.nextLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFRJREFUOMvd0skRgCAQBVEFwQ0V7fxzNQP6wI05v6pZ/kyj1b7FNgik2gQzzLcAwiUAigHOTwDHK4A1CmB5BJANJG1hQ9qafYcqFlZP3IFc9eVGrR+iIgkDQRUXIAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image to be used for the refresh button. + */ +Editor.refreshLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAolBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8ELnaCAAAANXRSTlMABfyE2QKU+dfNyyDyoVYKwnTv7N+6rntsYlFNQjEqEw316uSzf2c1JB3GvqebiVw6GAjQB4DQr10AAAE7SURBVDjLvZLXcoMwEABPIgRCx3TT3A3udqL//7UgAdGRcR4yk8k+idsdmgS/QyWEqD/axS2JDV33zlnzLHIzQ2MDq9OeJ3m8l76KKENYlxrmM/b65Ys1+8YxnTEZFIEY0vVhszFWfUGZDJpQTDznTgAe5k4XhQxILB7ruzBQn+kkyDXuHfRtjoYDEvH7J9Lz98dBZXXL94X0Ofco2PFlChKbjVzEdakoSlKjoNoqPYkJ/wUZAYwc+PpLj1Ei7+jdoBWlwQZoJv2H1w3CWgRvo7dd9DP5btgwCWz0M02+oVoxCcIWeY9PNmR6B++m9prMxYEISpCBYBlfy9bc745is7UUULAem1Ww7FfalsiA2uaJsgmWP3pQI9q9/yMLkaaHAp2fxhHff/cNq7dBdHXhGW7l+Mo2zU0Cf8knJ2xA0oJ8enwAAAAASUVORK5CYII='; + +/** + * Specifies the image to be used for the back button. + */ +Editor.backLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAclBMVEUAAAD////////////////+/v7////////////////////////////////////////////+/v7///////////////////////////////////////////////////////////////////////////////8vKLfTAAAAJXRSTlMACh7h9gby3NLIwzwZ55uVJgH57b+8tbCljYV1RRMQ46FrTzQw+vtxOQAAAJ5JREFUOMuF00cWgzAQA1DRDQFCbwFSdf8rZpdVrNH2z3tuMv7mldZQ2WN2yi8x+TT8JvyTkqvwpiKvwsOIrA1fWr+XGTklfj8dOQR+D3KyUF6QufBkJN0hfCazEv6sZBRCJDUcPasGKpu1RLtYE8lkHAPBQLoTsK/SfAyRw5FjAuhCzC2MSj0gJ+66lHatgXdKboD9tfREB5m9/+3iC9jHDYvsGNcUAAAAAElFTkSuQmCC'; + +/** + * Specifies the image to be used for the back button. + */ +Editor.fullscreenLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAllBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AJcWoAAAAMXRSTlMA+wIFxPWPCIb446tnUxmsoIykgxTe29jQnpKBe2MNsZhVTR/KyLuWbFhEPjUq7L9z+bQj+gAAAWxJREFUOMttk4l2gkAMRTODCO4FtQgIbnWpS9v8/881iZFh8R51NO8GJ+gAjMN8zuTRFSw04cIOHQcqFHH6oaQFGxf0jeBjEgB8Y52TpW9Ag4zB5QICWOtHrgwGuFZBcw+gPP0MFS7+iiD5inOmDIQS9sZgTwUzwEzyxhxHVEEU7NdDUXsqUPtqjIgR2IZSCT4upzSeIeOdcMHnfDsx3giPoezfU6MrQGB5//SckLEG2xYscK4GfnUFqaix39zrwooaOD/cXoYuvHKQIc7pzd3HVPusp6t2FAW/RmjMonbl8vwHDeZo/GkleJC7e+p5XA/rAq1X/V10wKag04rBpa2/d0LL4OYYceOEtsG5jyMntI1wS+N1BGcQBl/CoLoPOl9ABrW/BP53e1bwSJHHlkIVchJwmHwyyfJ4kIvEnKtwkxNSEct83KSChT7WiWgDZ3ccZ0BM4tloJow2YUAtifNT3njnyD+y/pMsnP4DN3Y4yl1Gyk0AAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.ctrlKey = (mxClient.IS_MAC) ? 'Cmd' : 'Ctrl'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.hintOffset = 20; + +/** + * Specifies if the diagram should be saved automatically if possible. Default + * is true. + */ +Editor.popupsAllowed = true; + +/** + * Editor inherits from mxEventSource + */ +mxUtils.extend(Editor, mxEventSource); + +/** + * Stores initial state of mxClient.NO_FO. + */ +Editor.prototype.originalNoForeignObject = mxClient.NO_FO; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' : + IMAGE_PATH + '/transparent.gif'; + +/** + * Specifies if the canvas should be extended in all directions. Default is true. + */ +Editor.prototype.extendCanvas = true; + +/** + * Specifies if the app should run in chromeless mode. Default is false. + * This default is only used if the contructor argument is null. + */ +Editor.prototype.chromeless = false; + +/** + * Specifies the order of OK/Cancel buttons in dialogs. Default is true. + * Cancel first is used on Macs, Windows/Confluence uses cancel last. + */ +Editor.prototype.cancelFirst = true; + +/** + * Specifies if the editor is enabled. Default is true. + */ +Editor.prototype.enabled = true; + +/** + * Contains the name which was used for the last save. Default value is null. + */ +Editor.prototype.filename = null; + +/** + * Contains the current modified state of the diagram. This is false for + * new diagrams and after the diagram was saved. + */ +Editor.prototype.modified = false; + +/** + * Specifies if the diagram should be saved automatically if possible. Default + * is true. + */ +Editor.prototype.autosave = true; + +/** + * Specifies the top spacing for the initial page view. Default is 0. + */ +Editor.prototype.initialTopSpacing = 0; + +/** + * Specifies the app name. Default is document.title. + */ +Editor.prototype.appName = document.title; + +/** + * + */ +Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/'; + +/** + * Default value for the graph container overflow style. + */ +Editor.prototype.defaultGraphOverflow = 'hidden'; + +/** + * Initializes the environment. + */ +Editor.prototype.init = function () { }; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.isChromelessView = function () { + return this.chromeless; +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.setAutosave = function (value) { + this.autosave = value; + this.fireEvent(new mxEventObject('autosaveChanged')); +}; + +/** + * + */ +Editor.prototype.getEditBlankUrl = function (params) { + return this.editBlankUrl + params; +} + +/** + * + */ +Editor.prototype.editAsNew = function (xml, title) { + var p = (title != null) ? '?title=' + encodeURIComponent(title) : ''; + + if (urlParams['ui'] != null) { + p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui']; + } + + if (typeof window.postMessage !== 'undefined' && + (document.documentMode == null || + document.documentMode >= 10)) { + var wnd = null; + + var l = mxUtils.bind(this, function (evt) { + if (evt.data == 'ready' && evt.source == wnd) { + mxEvent.removeListener(window, 'message', l); + wnd.postMessage(xml, '*'); + } + }); + + mxEvent.addListener(window, 'message', l); + wnd = this.graph.openLink(this.getEditBlankUrl( + p + ((p.length > 0) ? '&' : '?') + + 'client=1'), null, true); + } + else { + this.graph.openLink(this.getEditBlankUrl(p) + + '#R' + encodeURIComponent(xml)); + } +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.createGraph = function (themes, model) { + var graph = new Graph(null, model, null, null, themes); + graph.transparentBackground = false; + + // Opens all links in a new window while editing + if (!this.chromeless) { + graph.isBlankLink = function (href) { + return !this.isExternalProtocol(href); + }; + } + + return graph; +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.resetGraph = function () { + this.graph.gridEnabled = !this.isChromelessView() || urlParams['grid'] == '1'; + this.graph.graphHandler.guidesEnabled = true; + this.graph.setTooltips(true); + this.graph.setConnectable(true); + this.graph.foldingEnabled = true; + this.graph.scrollbars = this.graph.defaultScrollbars; + this.graph.pageVisible = this.graph.defaultPageVisible; + this.graph.pageBreaksVisible = this.graph.pageVisible; + this.graph.preferPageSize = this.graph.pageBreaksVisible; + this.graph.background = null; + this.graph.pageScale = mxGraph.prototype.pageScale; + this.graph.pageFormat = mxGraph.prototype.pageFormat; + this.graph.currentScale = 1; + this.graph.currentTranslate.x = 0; + this.graph.currentTranslate.y = 0; + this.updateGraphComponents(); + this.graph.view.setScale(1); +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.readGraphState = function (node) { + this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.isChromelessView() || urlParams['grid'] == '1'); + this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize; + this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0'; + this.graph.setTooltips(node.getAttribute('tooltips') != '0'); + this.graph.setConnectable(node.getAttribute('connect') != '0'); + this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0'; + this.graph.foldingEnabled = node.getAttribute('fold') != '0'; + + if (this.isChromelessView() && this.graph.foldingEnabled) { + this.graph.foldingEnabled = urlParams['nav'] == '1'; + this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled; + } + + var ps = parseFloat(node.getAttribute('pageScale')); + + if (!isNaN(ps) && ps > 0) { + this.graph.pageScale = ps; + } + else { + this.graph.pageScale = mxGraph.prototype.pageScale; + } + + if (!this.graph.isLightboxView() && !this.graph.isViewer()) { + var pv = node.getAttribute('page'); + + if (pv != null) { + this.graph.pageVisible = (pv != '0'); + } + else { + this.graph.pageVisible = this.graph.defaultPageVisible; + } + } + else { + this.graph.pageVisible = false; + } + + this.graph.pageBreaksVisible = this.graph.pageVisible; + this.graph.preferPageSize = this.graph.pageBreaksVisible; + + var pw = parseFloat(node.getAttribute('pageWidth')); + var ph = parseFloat(node.getAttribute('pageHeight')); + + if (!isNaN(pw) && !isNaN(ph)) { + this.graph.pageFormat = new mxRectangle(0, 0, pw, ph); + } + + // Loads the persistent state settings + var bg = node.getAttribute('background'); + + if (bg != null && bg.length > 0) { + this.graph.background = bg; + } + else { + this.graph.background = null; + } +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.setGraphXml = function (node) { + if (node != null) { + var dec = new mxCodec(node.ownerDocument); + + if (node.nodeName == 'mxGraphModel') { + this.graph.model.beginUpdate(); + + try { + this.graph.model.clear(); + this.graph.view.scale = 1; + this.readGraphState(node); + this.updateGraphComponents(); + dec.decode(node, this.graph.getModel()); + } + finally { + this.graph.model.endUpdate(); + } + + this.fireEvent(new mxEventObject('resetGraphView')); + } + else if (node.nodeName == 'root') { + this.resetGraph(); + + // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml + var wrapper = dec.document.createElement('mxGraphModel'); + wrapper.appendChild(node); + + dec.decode(wrapper, this.graph.getModel()); + this.updateGraphComponents(); + this.fireEvent(new mxEventObject('resetGraphView')); + } + else { + throw { + message: mxResources.get('cannotOpenFile'), + node: node, + toString: function () { return this.message; } + }; + } + } + else { + this.resetGraph(); + this.graph.model.clear(); + this.fireEvent(new mxEventObject('resetGraphView')); + } +}; + +/** + * Returns the XML node that represents the current diagram. + */ +Editor.prototype.getGraphXml = function (ignoreSelection) { + ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; + var node = null; + + if (ignoreSelection) { + var enc = new mxCodec(mxUtils.createXmlDocument()); + node = enc.encode(this.graph.getModel()); + } + else { + node = this.graph.encodeCells(mxUtils.sortCells(this.graph.model.getTopmostCells( + this.graph.getSelectionCells()))); + } + + if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0) { + node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100); + node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100); + } + + node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0'); + node.setAttribute('gridSize', this.graph.gridSize); + node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0'); + node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0'); + node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0'); + node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0'); + node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0'); + node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0'); + node.setAttribute('pageScale', this.graph.pageScale); + node.setAttribute('pageWidth', this.graph.pageFormat.width); + node.setAttribute('pageHeight', this.graph.pageFormat.height); + + if (this.graph.background != null) { + node.setAttribute('background', this.graph.background); + } + + return node; +}; + +/** + * Keeps the graph container in sync with the persistent graph state + */ +Editor.prototype.updateGraphComponents = function () { + var graph = this.graph; + + if (graph.container != null) { + graph.view.validateBackground(); + graph.container.style.overflow = (graph.scrollbars) ? 'auto' : this.defaultGraphOverflow; + + this.fireEvent(new mxEventObject('updateGraphComponents')); + } +}; + +/** + * Sets the modified flag. + */ +Editor.prototype.setModified = function (value) { + this.modified = value; +}; + +/** + * Sets the filename. + */ +Editor.prototype.setFilename = function (value) { + this.filename = value; +}; + +/** + * Creates and returns a new undo manager. + */ +Editor.prototype.createUndoManager = function () { + var graph = this.graph; + var undoMgr = new mxUndoManager(); + + this.undoListener = function (sender, evt) { + undoMgr.undoableEditHappened(evt.getProperty('edit')); + }; + + // Installs the command history + var listener = mxUtils.bind(this, function (sender, evt) { + this.undoListener.apply(this, arguments); + }); + + graph.getModel().addListener(mxEvent.UNDO, listener); + graph.getView().addListener(mxEvent.UNDO, listener); + + // Keeps the selection in sync with the history + var undoHandler = function (sender, evt) { + var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes, function (change) { + // Only selects changes to the cell hierarchy + return !(change instanceof mxChildChange); + }); + + if (cand.length > 0) { + var model = graph.getModel(); + var cells = []; + + for (var i = 0; i < cand.length; i++) { + if (graph.view.getState(cand[i]) != null) { + cells.push(cand[i]); + } + } + + graph.setSelectionCells(cells); + } + }; + + undoMgr.addListener(mxEvent.UNDO, undoHandler); + undoMgr.addListener(mxEvent.REDO, undoHandler); + + return undoMgr; +}; + +/** + * Adds basic stencil set (no namespace). + */ +Editor.prototype.initStencilRegistry = function () { }; + +/** + * Creates and returns a new undo manager. + */ +Editor.prototype.destroy = function () { + if (this.graph != null) { + this.graph.destroy(); + this.graph = null; + } +}; + +/** + * Class for asynchronously opening a new window and loading a file at the same + * time. This acts as a bridge between the open dialog and the new editor. + */ +OpenFile = function (done) { + this.producer = null; + this.consumer = null; + this.done = done; + this.args = null; +}; + +/** + * Registers the editor from the new window. + */ +OpenFile.prototype.setConsumer = function (value) { + this.consumer = value; + this.execute(); +}; + +/** + * Sets the data from the loaded file. + */ +OpenFile.prototype.setData = function () { + this.args = arguments; + this.execute(); +}; + +/** + * Displays an error message. + */ +OpenFile.prototype.error = function (msg) { + this.cancel(true); + mxUtils.alert(msg); +}; + +/** + * Consumes the data. + */ +OpenFile.prototype.execute = function () { + if (this.consumer != null && this.args != null) { + this.cancel(false); + this.consumer.apply(this, this.args); + } +}; + +/** + * Cancels the operation. + */ +OpenFile.prototype.cancel = function (cancel) { + if (this.done != null) { + this.done((cancel != null) ? cancel : true); + } +}; + +/** + * Basic dialogs that are available in the viewer (print dialog). + */ +function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent, onResize, ignoreBgClick) { + var dx = 0; + + if (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) { + // Adds padding as a workaround for box model in older IE versions + // This needs to match the total padding of geDialog in CSS + dx = 80; + } + + w += dx; + h += dx; + + var w0 = w; + var h0 = h; + + var ds = mxUtils.getDocumentSize(); + + // Workaround for print dialog offset in viewer lightbox + if (window.innerHeight != null) { + ds.height = window.innerHeight; + } + + var dh = ds.height; + var left = Math.max(1, Math.round((ds.width - w - 64) / 2)); + var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); + + // Keeps window size inside available space + if (!mxClient.IS_QUIRKS) { + elt.style.maxHeight = '100%'; + } + + w = (document.body != null) ? Math.min(w, document.body.scrollWidth - 64) : w; + h = Math.min(h, dh - 64); + + // Increments zIndex to put subdialogs and background over existing dialogs and background + if (editorUi.dialogs.length > 0) { + this.zIndex += editorUi.dialogs.length * 2; + } + + if (this.bg == null) { + this.bg = editorUi.createDiv('background'); + this.bg.style.position = 'absolute'; + this.bg.style.background = Dialog.backdropColor; + this.bg.style.height = dh + 'px'; + this.bg.style.right = '0px'; + this.bg.style.zIndex = this.zIndex - 2; + + mxUtils.setOpacity(this.bg, this.bgOpacity); + + if (mxClient.IS_QUIRKS) { + new mxDivResizer(this.bg); + } + } + + var origin = mxUtils.getDocumentScrollOrigin(document); + this.bg.style.left = origin.x + 'px'; + this.bg.style.top = origin.y + 'px'; + left += origin.x; + top += origin.y; + + if (modal) { + document.body.appendChild(this.bg); + } + + var div = editorUi.createDiv(transparent ? 'geTransDialog' : 'geDialog'); + var pos = this.getPosition(left, top, w, h); + left = pos.x; + top = pos.y; + + div.style.width = w + 'px'; + div.style.height = h + 'px'; + div.style.left = left + 'px'; + div.style.top = top + 'px'; + div.style.zIndex = this.zIndex; + + div.appendChild(elt); + document.body.appendChild(div); + + // Adds vertical scrollbars if needed + if (!noScroll && elt.clientHeight > div.clientHeight - 64) { + elt.style.overflowY = 'auto'; + } + + if (closable) { + var img = document.createElement('img'); + + img.setAttribute('src', Dialog.prototype.closeImage); + img.setAttribute('title', mxResources.get('close')); + img.className = 'geDialogClose'; + img.style.top = (top + 14) + 'px'; + img.style.left = (left + w + 38 - dx) + 'px'; + img.style.zIndex = this.zIndex; + + mxEvent.addListener(img, 'click', mxUtils.bind(this, function () { + editorUi.hideDialog(true); + })); + + document.body.appendChild(img); + this.dialogImg = img; + + if (!ignoreBgClick) { + var mouseDownSeen = false; + + mxEvent.addGestureListeners(this.bg, mxUtils.bind(this, function (evt) { + mouseDownSeen = true; + }), null, mxUtils.bind(this, function (evt) { + if (mouseDownSeen) { + editorUi.hideDialog(true); + mouseDownSeen = false; + } + })); + } + } + + this.resizeListener = mxUtils.bind(this, function () { + if (onResize != null) { + var newWH = onResize(); + + if (newWH != null) { + w0 = w = newWH.w; + h0 = h = newWH.h; + } + } + + var ds = mxUtils.getDocumentSize(); + dh = ds.height; + this.bg.style.height = dh + 'px'; + + left = Math.max(1, Math.round((ds.width - w - 64) / 2)); + top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); + w = (document.body != null) ? Math.min(w0, document.body.scrollWidth - 64) : w0; + h = Math.min(h0, dh - 64); + + var pos = this.getPosition(left, top, w, h); + left = pos.x; + top = pos.y; + + div.style.left = left + 'px'; + div.style.top = top + 'px'; + div.style.width = w + 'px'; + div.style.height = h + 'px'; + + // Adds vertical scrollbars if needed + if (!noScroll && elt.clientHeight > div.clientHeight - 64) { + elt.style.overflowY = 'auto'; + } + + if (this.dialogImg != null) { + this.dialogImg.style.top = (top + 14) + 'px'; + this.dialogImg.style.left = (left + w + 38 - dx) + 'px'; + } + }); + + mxEvent.addListener(window, 'resize', this.resizeListener); + + this.onDialogClose = onClose; + this.container = div; + + editorUi.editor.fireEvent(new mxEventObject('showDialog')); +}; + +/** + * + */ +Dialog.backdropColor = 'white'; + +/** + * + */ +Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 1; + +/** + * + */ +Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC'; + +/** + * + */ +Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg=='; + +/** + * + */ +Dialog.prototype.clearImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/clear.gif' : 'data:image/gif;base64,R0lGODlhDQAKAIABAMDAwP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUIzOEM1NzI4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUIzOEM1NzM4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QjM4QzU3MDg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QjM4QzU3MTg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAEALAAAAAANAAoAAAIXTGCJebD9jEOTqRlttXdrB32PJ2ncyRQAOw=='; + +/** + * + */ +Dialog.prototype.lockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/locked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCODExNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCODIxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3RjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI4MDE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvqMCFYAAAAVUExURZmZmb+/v7KysqysrMzMzLGxsf///4g8N1cAAAAHdFJOU////////wAaSwNGAAAAPElEQVR42lTMQQ4AIQgEwUa0//9kTQirOweYOgDqAMbZUr10AGlAwx4/BJ2QJ4U0L5brYjovvpv32xZgAHZaATFtMbu4AAAAAElFTkSuQmCC'; + +/** + * + */ +Dialog.prototype.unlockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/unlocked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCN0QxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCN0UxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3QjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI3QzE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkKMpVwAAAAYUExURZmZmbKysr+/v6ysrOXl5czMzLGxsf///zHN5lwAAAAIdFJOU/////////8A3oO9WQAAADxJREFUeNpUzFESACAEBNBVsfe/cZJU+8Mzs8CIABCidtfGOndnYsT40HDSiCcbPdoJo10o9aI677cpwACRoAF3dFNlswAAAABJRU5ErkJggg=='; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.bgOpacity = 80; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.getPosition = function (left, top) { + return new mxPoint(left, top); +}; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.close = function (cancel, isEsc) { + if (this.onDialogClose != null) { + if (this.onDialogClose(cancel, isEsc) == false) { + return false; + } + + this.onDialogClose = null; + } + + if (this.dialogImg != null) { + this.dialogImg.parentNode.removeChild(this.dialogImg); + this.dialogImg = null; + } + + if (this.bg != null && this.bg.parentNode != null) { + this.bg.parentNode.removeChild(this.bg); + } + + mxEvent.removeListener(window, 'resize', this.resizeListener); + this.container.parentNode.removeChild(this.container); +}; + +/** + * + */ +var ErrorDialog = function (editorUi, title, message, buttonText, fn, retry, buttonText2, fn2, hide, buttonText3, fn3) { + hide = (hide != null) ? hide : true; + + var div = document.createElement('div'); + div.style.textAlign = 'center'; + + if (title != null) { + var hd = document.createElement('div'); + hd.style.padding = '0px'; + hd.style.margin = '0px'; + hd.style.fontSize = '18px'; + hd.style.paddingBottom = '16px'; + hd.style.marginBottom = '10px'; + hd.style.borderBottom = '1px solid #c0c0c0'; + hd.style.color = 'gray'; + hd.style.whiteSpace = 'nowrap'; + hd.style.textOverflow = 'ellipsis'; + hd.style.overflow = 'hidden'; + mxUtils.write(hd, title); + hd.setAttribute('title', title); + div.appendChild(hd); + } + + var p2 = document.createElement('div'); + p2.style.lineHeight = '1.2em'; + p2.style.padding = '6px'; + p2.innerHTML = message; + div.appendChild(p2); + + var btns = document.createElement('div'); + btns.style.marginTop = '12px'; + btns.style.textAlign = 'center'; + + if (retry != null) { + var retryBtn = mxUtils.button(mxResources.get('tryAgain'), function () { + editorUi.hideDialog(); + retry(); + }); + retryBtn.className = 'geBtn'; + btns.appendChild(retryBtn); + + btns.style.textAlign = 'center'; + } + + if (buttonText3 != null) { + var btn3 = mxUtils.button(buttonText3, function () { + if (fn3 != null) { + fn3(); + } + }); + + btn3.className = 'geBtn'; + btns.appendChild(btn3); + } + + var btn = mxUtils.button(buttonText, function () { + if (hide) { + editorUi.hideDialog(); + } + + if (fn != null) { + fn(); + } + }); + + btn.className = 'geBtn'; + btns.appendChild(btn); + + if (buttonText2 != null) { + var mainBtn = mxUtils.button(buttonText2, function () { + if (hide) { + editorUi.hideDialog(); + } + + if (fn2 != null) { + fn2(); + } + }); + + mainBtn.className = 'geBtn gePrimaryBtn'; + btns.appendChild(mainBtn); + } + + this.init = function () { + btn.focus(); + }; + + div.appendChild(btns); + + this.container = div; +}; + +/** + * Constructs a new print dialog. + */ +var PrintDialog = function (editorUi, title) { + this.create(editorUi, title); +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.prototype.create = function (editorUi) { + var graph = editorUi.editor.graph; + var row, td; + + var table = document.createElement('table'); + table.style.width = '100%'; + table.style.height = '100%'; + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + var onePageCheckBox = document.createElement('input'); + onePageCheckBox.setAttribute('type', 'checkbox'); + td = document.createElement('td'); + td.setAttribute('colspan', '2'); + td.style.fontSize = '10pt'; + td.appendChild(onePageCheckBox); + + var span = document.createElement('span'); + mxUtils.write(span, ' ' + mxResources.get('fitPage')); + td.appendChild(span); + + mxEvent.addListener(span, 'click', function (evt) { + onePageCheckBox.checked = !onePageCheckBox.checked; + pageCountCheckBox.checked = !onePageCheckBox.checked; + mxEvent.consume(evt); + }); + + mxEvent.addListener(onePageCheckBox, 'change', function () { + pageCountCheckBox.checked = !onePageCheckBox.checked; + }); + + row.appendChild(td); + tbody.appendChild(row); + + row = row.cloneNode(false); + + var pageCountCheckBox = document.createElement('input'); + pageCountCheckBox.setAttribute('type', 'checkbox'); + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.appendChild(pageCountCheckBox); + + var span = document.createElement('span'); + mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':'); + td.appendChild(span); + + mxEvent.addListener(span, 'click', function (evt) { + pageCountCheckBox.checked = !pageCountCheckBox.checked; + onePageCheckBox.checked = !pageCountCheckBox.checked; + mxEvent.consume(evt); + }); + + row.appendChild(td); + + var pageCountInput = document.createElement('input'); + pageCountInput.setAttribute('value', '1'); + pageCountInput.setAttribute('type', 'number'); + pageCountInput.setAttribute('min', '1'); + pageCountInput.setAttribute('size', '4'); + pageCountInput.setAttribute('disabled', 'disabled'); + pageCountInput.style.width = '50px'; + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.appendChild(pageCountInput); + mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)'); + row.appendChild(td); + tbody.appendChild(row); + + mxEvent.addListener(pageCountCheckBox, 'change', function () { + if (pageCountCheckBox.checked) { + pageCountInput.removeAttribute('disabled'); + } + else { + pageCountInput.setAttribute('disabled', 'disabled'); + } + + onePageCheckBox.checked = !pageCountCheckBox.checked; + }); + + row = row.cloneNode(false); + + td = document.createElement('td'); + mxUtils.write(td, mxResources.get('pageScale') + ':'); + row.appendChild(td); + + td = document.createElement('td'); + var pageScaleInput = document.createElement('input'); + pageScaleInput.setAttribute('value', '100 %'); + pageScaleInput.setAttribute('size', '5'); + pageScaleInput.style.width = '50px'; + + td.appendChild(pageScaleInput); + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '20px'; + td.setAttribute('align', 'right'); + + // Overall scale for print-out to account for print borders in dialogs etc + function preview(print) { + var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked; + var printScale = parseInt(pageScaleInput.value) / 100; + + if (isNaN(printScale)) { + printScale = 1; + pageScaleInput.value = '100%'; + } + + // Workaround to match available paper size in actual print output + printScale *= 0.75; + + var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT; + var scale = 1 / graph.pageScale; + + if (autoOrigin) { + var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value); + + if (!isNaN(pageCount)) { + scale = mxUtils.getScaleForPageCount(pageCount, graph, pf); + } + } + + // Negative coordinates are cropped or shifted if page visible + var gb = graph.getGraphBounds(); + var border = 0; + var x0 = 0; + var y0 = 0; + + // Applies print scale + pf = mxRectangle.fromRectangle(pf); + pf.width = Math.ceil(pf.width * printScale); + pf.height = Math.ceil(pf.height * printScale); + scale *= printScale; + + // Starts at first visible page + if (!autoOrigin && graph.pageVisible) { + var layout = graph.getPageLayout(); + x0 -= layout.x * pf.width; + y0 -= layout.y * pf.height; + } + else { + autoOrigin = true; + } + + var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin); + preview.open(); + + if (print) { + PrintDialog.printPreview(preview); + } + }; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + if (PrintDialog.previewEnabled) { + var previewBtn = mxUtils.button(mxResources.get('preview'), function () { + editorUi.hideDialog(); + preview(false); + }); + previewBtn.className = 'geBtn'; + td.appendChild(previewBtn); + } + + var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function () { + editorUi.hideDialog(); + preview(true); + }); + printBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(printBtn); + + if (!editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + + table.appendChild(tbody); + this.container = table; +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.printPreview = function (preview) { + try { + if (preview.wnd != null) { + var printFn = function () { + preview.wnd.focus(); + preview.wnd.print(); + preview.wnd.close(); + }; + + // Workaround for Google Chrome which needs a bit of a + // delay in order to render the SVG contents + // Needs testing in production + if (mxClient.IS_GC) { + window.setTimeout(printFn, 500); + } + else { + printFn(); + } + } + } + catch (e) { + // ignores possible Access Denied + } +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.createPrintPreview = function (graph, scale, pf, border, x0, y0, autoOrigin) { + var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0); + preview.title = mxResources.get('preview'); + preview.printBackgroundImage = true; + preview.autoOrigin = autoOrigin; + var bg = graph.background; + + if (bg == null || bg == '' || bg == mxConstants.NONE) { + bg = '#ffffff'; + } + + preview.backgroundColor = bg; + + var writeHead = preview.writeHead; + + // Adds a border in the preview + preview.writeHead = function (doc) { + writeHead.apply(this, arguments); + + doc.writeln(''); + }; + + return preview; +}; + +/** + * Specifies if the preview button should be enabled. Default is true. + */ +PrintDialog.previewEnabled = true; + +//--------------------------------------------------------------------------------------------------- + +/** + * Constructs a new workflow dialog. + */ +var WorkFlowDialog = function (editorUi, title) { + this.create(editorUi, title); +}; + +/** + * Constructs a new workflow dialog. + */ +WorkFlowDialog.prototype.create = function (editorUi) { + + var div = document.createElement('div'); + div.style.width = '100%'; + div.style.height = '100%'; + + + var row = document.createElement('div'); + + //select choice + var select = document.createElement('div'); + select.style.marginBottom = '20px'; + var service = document.createElement('input'); + service.setAttribute('type', 'radio'); + service.setAttribute('id', 'serviceButton'); + + var spanService = document.createElement('span'); + mxUtils.write(spanService, ' ' + mxResources.get('service')); + + var task = document.createElement('input'); + task.setAttribute('type', 'radio'); + task.setAttribute('id', 'taskButton'); + task.checked = true; // Task is selected by default + var spanTask = document.createElement('span'); + mxUtils.write(spanTask, ' ' + mxResources.get('task')); + + select.appendChild(service); + select.appendChild(spanService); + select.appendChild(task); + select.appendChild(spanTask); + + function showViewService(isService){ + service.checked = isService + task.checked = !isService; + + if (isService) { + event.style.display = 'none'; + start.style.display = ''; + end.style.display = ''; + duration.style.display = 'none'; + cron.style.display = 'none'; + } else { + event.style.display = ''; + start.style.display = ''; + end.style.display = ''; + duration.style.display = ''; + cron.style.display = ''; + } + } + + var spanViewListener = function (evt) { + + isServiceClic = evt.currentTarget.innerText == "Service" + showViewService(isServiceClic) + + mxEvent.consume(evt); + }; + + mxEvent.addListener(spanService, 'click', spanViewListener); + mxEvent.addListener(spanTask, 'click', spanViewListener); + + row.appendChild(select); + div.appendChild(row); + row = row.cloneNode(false); + + //event + var event = document.createElement('div'); + event.style.marginBottom = '50px'; + var eventCheckBox = document.createElement('input'); + eventCheckBox.setAttribute('type', 'checkbox'); + var span = document.createElement('span'); + mxUtils.write(span, ' ' + mxResources.get('event')); + event.appendChild(eventCheckBox); + event.appendChild(span); + + var spanListener = function (evt) { + eventCheckBox.checked = !eventCheckBox.checked; + if (eventCheckBox.checked == true) { + eventName.disabled = ''; + } else { + eventName.disabled = 'true'; + } + }; + + mxEvent.addListener(span, 'click', spanListener); + + var eventName = document.createElement('input'); + eventName.type = 'text'; + eventName.disabled = 'true'; + eventName.placeholder = 'Event Name'; + eventName.style.width = '75%'; + eventName.style.marginLeft = '20px'; + event.appendChild(eventName); + eventName.onchange = function () { + if (eventName.value.length >= 1) { + eventName.className = 'valid'; + } else { + eventName.className = 'invalid'; + } + }; + + row.appendChild(event); + div.appendChild(row); + row = row.cloneNode(false); + + //Time + var time = document.createElement('div'); + time.style.marginBottom = '50px'; + + var start = document.createElement('div'); + var startInputTitle = document.createTextNode('Start'); + var startDateInput = document.createElement('input'); + startDateInput.type = 'date'; + startDateInput.setAttribute('id', 'startDateInput'); + start.appendChild(startInputTitle); + start.appendChild(startDateInput); + + + var end = document.createElement('div'); + var endInputTitle = document.createTextNode('End'); + var endDateInput = document.createElement('input'); + endDateInput.type = 'date'; + endDateInput.setAttribute('id', 'endDateInput'); + end.appendChild(endInputTitle); + end.appendChild(endDateInput); + + var duration = document.createElement('div'); + var durationInputTitle = document.createTextNode('Duration'); + var durationInput = document.createElement('input'); + durationInput.type = 'number'; + durationInput.min = '0'; + durationInput.placeholder = 'in seconds'; + duration.appendChild(durationInputTitle); + duration.appendChild(durationInput); + + time.appendChild(start); + time.appendChild(end); + time.appendChild(duration); + + + row.appendChild(time); + div.appendChild(row); + row = row.cloneNode(false); + + + //cronInput + var cron = document.createElement('div'); + cron.style.marginBottom = '20px'; + var cronInput = document.createElement('input'); + var cronInputTitle = document.createTextNode('Schedule : '); + cronInput.type = 'text'; + cronInput.placeholder = '* * * * * * (Cron Syntax)'; + + var cronInfo = document.createElement('a'); + var cronInfoLink = document.createTextNode(' ?'); + cronInfo.appendChild(cronInfoLink); + cronInfo.title = 'Cron Info'; + cronInfo.href = 'http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-06.html'; + cronInfo.target = '_blank'; + cronInfo.style.color = '#000000'; + cronInfo.style.textDecoration = 'underline dotted'; + cronInfo.style.cursor = 'help'; + + cron.appendChild(cronInputTitle); + cron.appendChild(cronInput); + cron.appendChild(cronInfo); + + + + row.appendChild(cron); + div.appendChild(row); + row = row.cloneNode(false); + + //Buttons + var buttons = document.createElement('div'); + buttons.style.marginBottom = '30px'; + + var bookBtn = mxUtils.button(mxResources.get('book'), function () { + var event = eventName.value + var start = startDateInput.value + var end = endDateInput.value + var duration = durationInput.value + var cron = cronInput.value + + OC_CATALOG_API.then(client => { + client.apis + .workflow + .WorkflowController_Book_Schedule({ + workflowName: OC_PROJECT_NAME, + event: event, + start: start, + end: end, + duration: duration, + cron: cron + }) + .then(response =>{ + // console.log("send") + allBooked = true + for (const i in response.body) { + dc = response.body[i] + if (!dc.Booked) { + allBooked = false + displayMessage([dc.DCname + " - " + dc.ErrorMessage], 13000) + } + } + if (allBooked) { + disableAll() + } + }) + .catch((error) => { + console.log(error) + displayMessage([error.response.data]) + }); + }); + }); + bookBtn.className = 'btn disabled'; + + function displayMessage(messages, timeout=3000) { + + messages.forEach(m =>{ + var p = document.createElement('p'); + p.style.color = 'red'; + p.innerHTML = m; + response.appendChild(p) + }) + + setTimeout(() => { response.innerHTML = ""; }, timeout); + + } + + var checkBtn = mxUtils.button(mxResources.get('check'), function () { + OC_CATALOG_API.then(client => { + client.apis + .workflow + .WorkflowController_Check_Schedule({ + workflowName: OC_PROJECT_NAME, + }) + .then(answerOC =>{ + + answerOC.body + .sort((a,b) => a.DCname.localeCompare(b.DCname)) + .forEach(elem => { + responseServ(elem.IsAvailable, elem.ErrorMessage, elem.DCname) + }) + + }) + .catch((error) => { + console.log(error) + displayMessage([error.response.data]) + }); + }); + + response.innerHTML = ""; + }); + checkBtn.className = 'btn'; + checkBtn.style.marginRight = '100px'; + checkBtn.style.marginLeft = '5px'; + checkBtn.disabled = true + + function setScheduleConfig() { + var swaggerArgs = { + workflowName: OC_PROJECT_NAME, + isService: document.getElementById("serviceButton").checked, + } + + if (!startDateInput.value) { + startDateInput.classList.remove("valid"); + startDateInput.classList.add("invalid"); + setTimeout(function() { + startDateInput.classList.remove("invalid");; + }, 1000); + return + } + swaggerArgs["startDate"] = new Date(startDateInput.value).toISOString() + + + if (!endDateInput.value) { + endDateInput.classList.remove("valid"); + endDateInput.classList.add("invalid"); + setTimeout(function() { + endDateInput.classList.remove("invalid");; + }, 1000); + return + } + + swaggerArgs["stopDate"] = new Date(endDateInput.value).toISOString() + + + if (!swaggerArgs.isService){ // if task + if (eventCheckBox.checked) { + swaggerArgs["events"] = eventName.value + } + + if (!Number.isInteger(durationInput.valueAsNumber) || durationInput.valueAsNumber <= 0) { + durationInput.classList.remove("valid"); + durationInput.classList.add("invalid"); + setTimeout(function() { + durationInput.classList.remove("invalid");; + }, 1000); + return + } + swaggerArgs["duration"] = durationInput.valueAsNumber + swaggerArgs["cronString"] = cronInput.value + } + + + OC_CATALOG_API.then(client => { + client.apis + .workflow + .WorkflowController_Set_Schedule(swaggerArgs) + .then(answer =>{ + // Task or service + service.checked = answer.body.IsService; + task.checked = !service.checked; + checkBtn.disabled = false + // editorUi.hideDialog(); + // console.log(OC_CATALOG_API) + }) + .catch((error) => { + console.log(error) + displayMessage([error.response.data]) + }); + }); + }; + + + var saveBtn = mxUtils.button('SAVE', function () { + setScheduleConfig(); + }); + saveBtn.className = 'btn'; + saveBtn.style.marginRight = '135px'; + + + var closeBtn = mxUtils.button(mxResources.get('close'), function () { + // setScheduleConfig(); + editorUi.hideDialog(); + }); + + + + + closeBtn.className = 'btn'; + buttons.appendChild(bookBtn); + buttons.appendChild(checkBtn); + buttons.appendChild(saveBtn); + buttons.appendChild(closeBtn); + + row.appendChild(buttons); + + //Response + var response = document.createElement('div'); + response.style.height = "150px" + response.style.overflow = 'auto' + + if(MX_EDITORUI.editor.errorlogs_array != null){ + MX_EDITORUI.editor.errorlogs_array.forEach(elem =>{ + var p = document.createElement('p'); + p.style.color = 'red'; + p.innerHTML = elem + response.appendChild(p) + checkBtn.className = 'btn disabled'; + saveBtn.className = 'btn disabled'; + }); + } + console.log(response) + + + + function responseServ(available, messageError, DCName) { + if(available == true){ + var p = document.createElement('p'); + p.style.color = 'green'; + p.innerHTML = DCName + ": ok"; + console.log(DCName + ": ok"); + bookBtn.className = 'btn' + response.appendChild(p) + + }else if (available == false){ + var p = document.createElement('p'); + p.style.color = 'red'; + p.innerHTML = DCName + ": " + messageError; + bookBtn.className = 'btn disabled' + response.appendChild(p); + } + } + + row.appendChild(response); + div.appendChild(row); + this.container = div; + + function disableAll() { + startDateInput.disabled = true + endDateInput.disabled = true + durationInput.disabled = true + cronInput.disabled = true + service.disabled = true + task.disabled = true + bookBtn.disabled = true + checkBtn.disabled = true + saveBtn.disabled = true + event.disabled = true + eventCheckBox.disabled = true + + //remove actions + mxEvent.removeListener(span, 'click', spanListener); + mxEvent.removeListener(spanService, 'click', spanViewListener); + mxEvent.removeListener(spanTask, 'click', spanViewListener); + + var p = document.createElement('p'); + p.style.color = 'green'; + p.innerHTML = "Workflow booked correctly"; + // console.log(DCName + ": ok"); + bookBtn.className = 'btn' + response.appendChild(p) + } + + OC_CATALOG_API.then(client => { + client.apis + .workflow + .WorkflowController_Get_Schedule({ + workflowName: OC_PROJECT_NAME + }) + .then(answer =>{ + // editorUi.hideDialog(); + console.log(answer.body) + showViewService(answer.body.IsService) + + sd = new Date(answer.body.StartDate) + if ( sd.getFullYear() > 1) { + startDateInput.value = sd.toISOString().split('T')[0] + } + + ed = new Date(answer.body.StopDate) + if ( ed.getFullYear() > 1 ) { + endDateInput.value = ed.toISOString().split('T')[0] + } + + if (!answer.body.IsService) { + durationInput.value = answer.body.duration + cronInput.value = answer.body.cron + } + + if (answer.body.isBooked) { + disableAll() + } + }) + .catch((error) => { + console.log(error) + }); + }); +}; +//--------------------------------------------------------------------------------------------------- + + +/** + * Constructs a new page setup dialog. + */ +var PageSetupDialog = function (editorUi) { + var graph = editorUi.editor.graph; + var row, td; + + var table = document.createElement('table'); + table.style.width = '100%'; + table.style.height = '100%'; + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.verticalAlign = 'top'; + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('paperSize') + ':'); + + row.appendChild(td); + + td = document.createElement('td'); + td.style.verticalAlign = 'top'; + td.style.fontSize = '10pt'; + + var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + mxUtils.write(td, mxResources.get('background') + ':'); + + row.appendChild(td); + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + + var backgroundInput = document.createElement('input'); + backgroundInput.setAttribute('type', 'text'); + var backgroundButton = document.createElement('button'); + + backgroundButton.style.width = '18px'; + backgroundButton.style.height = '18px'; + backgroundButton.style.marginRight = '20px'; + backgroundButton.style.backgroundPosition = 'center center'; + backgroundButton.style.backgroundRepeat = 'no-repeat'; + + var newBackgroundColor = graph.background; + + function updateBackgroundColor() { + if (newBackgroundColor == null || newBackgroundColor == mxConstants.NONE) { + backgroundButton.style.backgroundColor = ''; + backgroundButton.style.backgroundImage = 'url(\'' + Dialog.prototype.noColorImage + '\')'; + } + else { + backgroundButton.style.backgroundColor = newBackgroundColor; + backgroundButton.style.backgroundImage = ''; + } + }; + + updateBackgroundColor(); + + mxEvent.addListener(backgroundButton, 'click', function (evt) { + editorUi.pickColor(newBackgroundColor || 'none', function (color) { + newBackgroundColor = color; + updateBackgroundColor(); + }); + mxEvent.consume(evt); + }); + + td.appendChild(backgroundButton); + + mxUtils.write(td, mxResources.get('gridSize') + ':'); + + var gridSizeInput = document.createElement('input'); + gridSizeInput.setAttribute('type', 'number'); + gridSizeInput.setAttribute('min', '0'); + gridSizeInput.style.width = '40px'; + gridSizeInput.style.marginLeft = '6px'; + + gridSizeInput.value = graph.getGridSize(); + td.appendChild(gridSizeInput); + + mxEvent.addListener(gridSizeInput, 'change', function () { + var value = parseInt(gridSizeInput.value); + gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value); + }); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + + mxUtils.write(td, mxResources.get('image') + ':'); + + row.appendChild(td); + td = document.createElement('td'); + + var changeImageLink = document.createElement('a'); + changeImageLink.style.textDecoration = 'underline'; + changeImageLink.style.cursor = 'pointer'; + changeImageLink.style.color = '#a0a0a0'; + + var newBackgroundImage = graph.backgroundImage; + + function updateBackgroundImage() { + if (newBackgroundImage == null) { + changeImageLink.removeAttribute('title'); + changeImageLink.style.fontSize = ''; + changeImageLink.innerHTML = mxResources.get('change') + '...'; + } + else { + changeImageLink.setAttribute('title', newBackgroundImage.src); + changeImageLink.style.fontSize = '11px'; + changeImageLink.innerHTML = newBackgroundImage.src.substring(0, 42) + '...'; + } + }; + + mxEvent.addListener(changeImageLink, 'click', function (evt) { + editorUi.showBackgroundImageDialog(function (image) { + newBackgroundImage = image; + updateBackgroundImage(); + }); + + mxEvent.consume(evt); + }); + + updateBackgroundImage(); + + td.appendChild(changeImageLink); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '16px'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + var applyBtn = mxUtils.button(mxResources.get('apply'), function () { + editorUi.hideDialog(); + + if (graph.gridSize !== gridSizeInput.value) { + graph.setGridSize(parseInt(gridSizeInput.value)); + } + + var change = new ChangePageSetup(editorUi, newBackgroundColor, + newBackgroundImage, accessor.get()); + change.ignoreColor = graph.background == newBackgroundColor; + + var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null; + var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null; + + change.ignoreImage = oldSrc === newSrc; + + if (graph.pageFormat.width != change.previousFormat.width || + graph.pageFormat.height != change.previousFormat.height || + !change.ignoreColor || !change.ignoreImage) { + graph.model.execute(change); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(applyBtn); + + if (!editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + + table.appendChild(tbody); + this.container = table; +}; + +/** + * + */ +PageSetupDialog.addPageFormatPanel = function (div, namePostfix, pageFormat, pageFormatListener) { + var formatName = 'format-' + namePostfix; + + var portraitCheckBox = document.createElement('input'); + portraitCheckBox.setAttribute('name', formatName); + portraitCheckBox.setAttribute('type', 'radio'); + portraitCheckBox.setAttribute('value', 'portrait'); + + var landscapeCheckBox = document.createElement('input'); + landscapeCheckBox.setAttribute('name', formatName); + landscapeCheckBox.setAttribute('type', 'radio'); + landscapeCheckBox.setAttribute('value', 'landscape'); + + var paperSizeSelect = document.createElement('select'); + paperSizeSelect.style.marginBottom = '8px'; + paperSizeSelect.style.width = '202px'; + + var formatDiv = document.createElement('div'); + formatDiv.style.marginLeft = '4px'; + formatDiv.style.width = '210px'; + formatDiv.style.height = '24px'; + + portraitCheckBox.style.marginRight = '6px'; + formatDiv.appendChild(portraitCheckBox); + + var portraitSpan = document.createElement('span'); + portraitSpan.style.maxWidth = '100px'; + mxUtils.write(portraitSpan, mxResources.get('portrait')); + formatDiv.appendChild(portraitSpan); + + landscapeCheckBox.style.marginLeft = '10px'; + landscapeCheckBox.style.marginRight = '6px'; + formatDiv.appendChild(landscapeCheckBox); + + var landscapeSpan = document.createElement('span'); + landscapeSpan.style.width = '100px'; + mxUtils.write(landscapeSpan, mxResources.get('landscape')); + formatDiv.appendChild(landscapeSpan) + + var customDiv = document.createElement('div'); + customDiv.style.marginLeft = '4px'; + customDiv.style.width = '210px'; + customDiv.style.height = '24px'; + + var widthInput = document.createElement('input'); + widthInput.setAttribute('size', '7'); + widthInput.style.textAlign = 'right'; + customDiv.appendChild(widthInput); + mxUtils.write(customDiv, ' in x '); + + var heightInput = document.createElement('input'); + heightInput.setAttribute('size', '7'); + heightInput.style.textAlign = 'right'; + customDiv.appendChild(heightInput); + mxUtils.write(customDiv, ' in'); + + formatDiv.style.display = 'none'; + customDiv.style.display = 'none'; + + var pf = new Object(); + var formats = PageSetupDialog.getFormats(); + + for (var i = 0; i < formats.length; i++) { + var f = formats[i]; + pf[f.key] = f; + + var paperSizeOption = document.createElement('option'); + paperSizeOption.setAttribute('value', f.key); + mxUtils.write(paperSizeOption, f.title); + paperSizeSelect.appendChild(paperSizeOption); + } + + var customSize = false; + + function listener(sender, evt, force) { + if (force || (widthInput != document.activeElement && heightInput != document.activeElement)) { + var detected = false; + + for (var i = 0; i < formats.length; i++) { + var f = formats[i]; + + // Special case where custom was chosen + if (customSize) { + if (f.key == 'custom') { + paperSizeSelect.value = f.key; + customSize = false; + } + } + else if (f.format != null) { + // Fixes wrong values for previous A4 and A5 page sizes + if (f.key == 'a4') { + if (pageFormat.width == 826) { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.width = 827; + } + else if (pageFormat.height == 826) { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.height = 827; + } + } + else if (f.key == 'a5') { + if (pageFormat.width == 584) { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.width = 583; + } + else if (pageFormat.height == 584) { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.height = 583; + } + } + + if (pageFormat.width == f.format.width && pageFormat.height == f.format.height) { + paperSizeSelect.value = f.key; + portraitCheckBox.setAttribute('checked', 'checked'); + portraitCheckBox.defaultChecked = true; + portraitCheckBox.checked = true; + landscapeCheckBox.removeAttribute('checked'); + landscapeCheckBox.defaultChecked = false; + landscapeCheckBox.checked = false; + detected = true; + } + else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width) { + paperSizeSelect.value = f.key; + portraitCheckBox.removeAttribute('checked'); + portraitCheckBox.defaultChecked = false; + portraitCheckBox.checked = false; + landscapeCheckBox.setAttribute('checked', 'checked'); + landscapeCheckBox.defaultChecked = true; + landscapeCheckBox.checked = true; + detected = true; + } + } + } + + // Selects custom format which is last in list + if (!detected) { + widthInput.value = pageFormat.width / 100; + heightInput.value = pageFormat.height / 100; + portraitCheckBox.setAttribute('checked', 'checked'); + paperSizeSelect.value = 'custom'; + formatDiv.style.display = 'none'; + customDiv.style.display = ''; + } + else { + formatDiv.style.display = ''; + customDiv.style.display = 'none'; + } + } + }; + + listener(); + + div.appendChild(paperSizeSelect); + mxUtils.br(div); + + div.appendChild(formatDiv); + div.appendChild(customDiv); + + var currentPageFormat = pageFormat; + + var update = function (evt, selectChanged) { + var f = pf[paperSizeSelect.value]; + + if (f.format != null) { + widthInput.value = f.format.width / 100; + heightInput.value = f.format.height / 100; + customDiv.style.display = 'none'; + formatDiv.style.display = ''; + } + else { + formatDiv.style.display = 'none'; + customDiv.style.display = ''; + } + + var wi = parseFloat(widthInput.value); + + if (isNaN(wi) || wi <= 0) { + widthInput.value = pageFormat.width / 100; + } + + var hi = parseFloat(heightInput.value); + + if (isNaN(hi) || hi <= 0) { + heightInput.value = pageFormat.height / 100; + } + + var newPageFormat = new mxRectangle(0, 0, + Math.floor(parseFloat(widthInput.value) * 100), + Math.floor(parseFloat(heightInput.value) * 100)); + + if (paperSizeSelect.value != 'custom' && landscapeCheckBox.checked) { + newPageFormat = new mxRectangle(0, 0, newPageFormat.height, newPageFormat.width); + } + + // Initial select of custom should not update page format to avoid update of combo + if ((!selectChanged || !customSize) && (newPageFormat.width != currentPageFormat.width || + newPageFormat.height != currentPageFormat.height)) { + currentPageFormat = newPageFormat; + + // Updates page format and reloads format panel + if (pageFormatListener != null) { + pageFormatListener(currentPageFormat); + } + } + }; + + mxEvent.addListener(portraitSpan, 'click', function (evt) { + portraitCheckBox.checked = true; + update(evt); + mxEvent.consume(evt); + }); + + mxEvent.addListener(landscapeSpan, 'click', function (evt) { + landscapeCheckBox.checked = true; + update(evt); + mxEvent.consume(evt); + }); + + mxEvent.addListener(widthInput, 'blur', update); + mxEvent.addListener(widthInput, 'click', update); + mxEvent.addListener(heightInput, 'blur', update); + mxEvent.addListener(heightInput, 'click', update); + mxEvent.addListener(landscapeCheckBox, 'change', update); + mxEvent.addListener(portraitCheckBox, 'change', update); + mxEvent.addListener(paperSizeSelect, 'change', function (evt) { + // Handles special case where custom was chosen + customSize = paperSizeSelect.value == 'custom'; + update(evt, true); + }); + + update(); + + return { + set: function (value) { + pageFormat = value; + listener(null, null, true); + }, get: function () { + return currentPageFormat; + }, widthInput: widthInput, + heightInput: heightInput + }; +}; + +/** + * + */ +PageSetupDialog.getFormats = function () { + return [{ key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT }, + { key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400) }, + { key: 'tabloid', title: 'US-Tabloid (11" x 17")', format: new mxRectangle(0, 0, 1100, 1700) }, + { key: 'executive', title: 'US-Executive (7" x 10")', format: new mxRectangle(0, 0, 700, 1000) }, + { key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681) }, + { key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300) }, + { key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336) }, + { key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654) }, + { key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT }, + { key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827) }, + { key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583) }, + { key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413) }, + { key: 'b4', title: 'B4 (250 mm x 353 mm)', format: new mxRectangle(0, 0, 980, 1390) }, + { key: 'b5', title: 'B5 (176 mm x 250 mm)', format: new mxRectangle(0, 0, 690, 980) }, + { key: '16-9', title: '16:9 (1600 x 900)', format: new mxRectangle(0, 0, 1600, 900) }, + { key: '16-10', title: '16:10 (1920 x 1200)', format: new mxRectangle(0, 0, 1920, 1200) }, + { key: '4-3', title: '4:3 (1600 x 1200)', format: new mxRectangle(0, 0, 1600, 1200) }, + { key: 'custom', title: mxResources.get('custom'), format: null }]; +}; + +/** + * Constructs a new filename dialog. + */ +var FilenameDialog = function (editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn, hints, w) { + closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.style.marginTop = '8px'; + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + td.style.fontSize = '10pt'; + td.style.width = '120px'; + mxUtils.write(td, (label || mxResources.get('filename')) + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', filename || ''); + nameInput.style.marginLeft = '4px'; + nameInput.style.width = (w != null) ? w + 'px' : '180px'; + + var genericBtn = mxUtils.button(buttonText, function () { + if (validateFn == null || validateFn(nameInput.value)) { + if (closeOnBtn) { + editorUi.hideDialog(); + } + + fn(nameInput.value); + } + }); + genericBtn.className = 'geBtn gePrimaryBtn'; + + this.init = function () { + if (label == null && content != null) { + return; + } + + nameInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) { + nameInput.select(); + } + else { + document.execCommand('selectAll', false, null); + } + + // Installs drag and drop handler for links + if (Graph.fileSupport) { + // Setup the dnd listeners + var dlg = table.parentNode; + + if (dlg != null) { + var graph = editorUi.editor.graph; + var dropElt = null; + + mxEvent.addListener(dlg, 'dragleave', function (evt) { + if (dropElt != null) { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function (evt) { + // IE 10 does not implement pointer-events so it can't have a drop highlight + if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10)) { + dropElt = nameInput; + dropElt.style.backgroundColor = '#ebf2f9'; + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + + mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function (evt) { + if (dropElt != null) { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) { + nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list')); + genericBtn.click(); + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + } + } + }; + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + td.appendChild(nameInput); + row.appendChild(td); + + if (label != null || content == null) { + tbody.appendChild(row); + + if (hints != null) { + td.appendChild(FilenameDialog.createTypeHint(editorUi, nameInput, hints)); + } + } + + if (content != null) { + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.appendChild(content); + row.appendChild(td); + tbody.appendChild(row); + } + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '20px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () { + editorUi.hideDialog(); + + if (cancelFn != null) { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + if (helpLink != null) { + var helpBtn = mxUtils.button(mxResources.get('help'), function () { + editorUi.editor.graph.openLink(helpLink); + }); + + helpBtn.className = 'geBtn'; + td.appendChild(helpBtn); + } + + mxEvent.addListener(nameInput, 'keypress', function (e) { + if (e.keyCode == 13) { + genericBtn.click(); + } + }); + + td.appendChild(genericBtn); + + if (!editorUi.editor.cancelFirst) { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + + this.container = table; +}; + +/** + * + */ +FilenameDialog.filenameHelpLink = null; + +/** + * + */ +FilenameDialog.createTypeHint = function (ui, nameInput, hints) { + var hint = document.createElement('img'); + hint.style.cssText = 'vertical-align:top;height:16px;width:16px;margin-left:4px;background-repeat:no-repeat;background-position:center bottom;cursor:pointer;'; + mxUtils.setOpacity(hint, 70); + + var nameChanged = function () { + hint.setAttribute('src', Editor.helpImage); + hint.setAttribute('title', mxResources.get('help')); + + for (var i = 0; i < hints.length; i++) { + if (hints[i].ext.length > 0 && + nameInput.value.substring(nameInput.value.length - + hints[i].ext.length - 1) == '.' + hints[i].ext) { + hint.setAttribute('src', mxClient.imageBasePath + '/warning.png'); + hint.setAttribute('title', mxResources.get(hints[i].title)); + break; + } + } + }; + + mxEvent.addListener(nameInput, 'keyup', nameChanged); + mxEvent.addListener(nameInput, 'change', nameChanged); + mxEvent.addListener(hint, 'click', function (evt) { + var title = hint.getAttribute('title'); + + if (hint.getAttribute('src') == Editor.helpImage) { + ui.editor.graph.openLink(FilenameDialog.filenameHelpLink); + } + else if (title != '') { + ui.showError(null, title, mxResources.get('help'), function () { + ui.editor.graph.openLink(FilenameDialog.filenameHelpLink); + }, null, mxResources.get('ok'), null, null, null, 340, 90); + } + + mxEvent.consume(evt); + }); + + nameChanged(); + + return hint; +}; + +/** + * Static overrides + */ +(function () { + // Uses HTML for background pages (to support grid background image) + mxGraphView.prototype.validateBackgroundPage = function () { + var graph = this.graph; + + if (graph.container != null && !graph.transparentBackground) { + if (graph.pageVisible) { + var bounds = this.getBackgroundPageBounds(); + + if (this.backgroundPageShape == null) { + // Finds first element in graph container + var firstChild = graph.container.firstChild; + + while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT) { + firstChild = firstChild.nextSibling; + } + + if (firstChild != null) { + this.backgroundPageShape = this.createBackgroundPageShape(bounds); + this.backgroundPageShape.scale = 1; + + // Shadow filter causes problems in outline window in quirks mode. IE8 standards + // also has known rendering issues inside mxWindow but not using shadow is worse. + this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS; + this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML; + this.backgroundPageShape.init(graph.container); + + // Required for the browser to render the background page in correct order + firstChild.style.position = 'absolute'; + graph.container.insertBefore(this.backgroundPageShape.node, firstChild); + this.backgroundPageShape.redraw(); + + this.backgroundPageShape.node.className = 'geBackgroundPage'; + + // Adds listener for double click handling on background + mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', + mxUtils.bind(this, function (evt) { + graph.dblClick(evt); + }) + ); + + // Adds basic listeners for graph event dispatching outside of the + // container and finishing the handling of a single gesture + mxEvent.addGestureListeners(this.backgroundPageShape.node, + mxUtils.bind(this, function (evt) { + graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt)); + }), + mxUtils.bind(this, function (evt) { + // Hides the tooltip if mouse is outside container + if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) { + graph.tooltipHandler.hide(); + } + + if (graph.isMouseDown && !mxEvent.isConsumed(evt)) { + graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt)); + } + }), + mxUtils.bind(this, function (evt) { + graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt)); + }) + ); + } + } + else { + this.backgroundPageShape.scale = 1; + this.backgroundPageShape.bounds = bounds; + this.backgroundPageShape.redraw(); + } + } + else if (this.backgroundPageShape != null) { + this.backgroundPageShape.destroy(); + this.backgroundPageShape = null; + } + + this.validateBackgroundStyles(); + } + }; + + // Updates the CSS of the background to draw the grid + mxGraphView.prototype.validateBackgroundStyles = function () { + var graph = this.graph; + var color = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; + var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff'; + var image = 'none'; + var position = ''; + + if (graph.isGridEnabled()) { + var phase = 10; + + if (mxClient.IS_SVG) { + // Generates the SVG required for drawing the dynamic grid + image = unescape(encodeURIComponent(this.createSvgGrid(gridColor))); + image = (window.btoa) ? btoa(image) : Base64.encode(image, true); + image = 'url(' + 'data:image/svg+xml;base64,' + image + ')' + phase = graph.gridSize * this.scale * this.gridSteps; + } + else { + // Fallback to grid wallpaper with fixed size + image = 'url(' + this.gridImage + ')'; + } + + var x0 = 0; + var y0 = 0; + + if (graph.view.backgroundPageShape != null) { + var bds = this.getBackgroundPageBounds(); + + x0 = 1 + bds.x; + y0 = 1 + bds.y; + } + + // Computes the offset to maintain origin for grid + position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' + + -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px'; + } + + var canvas = graph.view.canvas; + + if (canvas.ownerSVGElement != null) { + canvas = canvas.ownerSVGElement; + } + + if (graph.view.backgroundPageShape != null) { + graph.view.backgroundPageShape.node.style.backgroundPosition = position; + graph.view.backgroundPageShape.node.style.backgroundImage = image; + graph.view.backgroundPageShape.node.style.backgroundColor = color; + graph.container.className = 'geDiagramContainer geDiagramBackdrop'; + canvas.style.backgroundImage = 'none'; + canvas.style.backgroundColor = ''; + } + else { + graph.container.className = 'geDiagramContainer'; + canvas.style.backgroundPosition = position; + canvas.style.backgroundColor = color; + canvas.style.backgroundImage = image; + } + }; + + // Returns the SVG required for painting the background grid. + mxGraphView.prototype.createSvgGrid = function (color) { + var tmp = this.graph.gridSize * this.scale; + + while (tmp < this.minGridSize) { + tmp *= 2; + } + + var tmp2 = this.gridSteps * tmp; + + // Small grid lines + var d = []; + + for (var i = 1; i < this.gridSteps; i++) { + var tmp3 = i * tmp; + d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2); + } + + // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround + // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams. + var size = tmp2; + var svg = '' + + '' + + '' + + '' + + ''; + + return svg; + }; + + // Adds panning for the grid with no page view and disabled scrollbars + var mxGraphPanGraph = mxGraph.prototype.panGraph; + mxGraph.prototype.panGraph = function (dx, dy) { + mxGraphPanGraph.apply(this, arguments); + + if (this.shiftPreview1 != null) { + var canvas = this.view.canvas; + + if (canvas.ownerSVGElement != null) { + canvas = canvas.ownerSVGElement; + } + + var phase = this.gridSize * this.view.scale * this.view.gridSteps; + var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' + + -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px'; + canvas.style.backgroundPosition = position; + } + }; + + // Draws page breaks only within the page + mxGraph.prototype.updatePageBreaks = function (visible, width, height) { + var scale = this.view.scale; + var tr = this.view.translate; + var fmt = this.pageFormat; + var ps = scale * this.pageScale; + + var bounds2 = this.view.getBackgroundPageBounds(); + + width = bounds2.width; + height = bounds2.height; + var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps); + + // Does not show page breaks if the scale is too small + visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist; + + var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0; + var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0; + var right = bounds2.x + width; + var bottom = bounds2.y + height; + + if (this.horizontalPageBreaks == null && horizontalCount > 0) { + this.horizontalPageBreaks = []; + } + + if (this.verticalPageBreaks == null && verticalCount > 0) { + this.verticalPageBreaks = []; + } + + var drawPageBreaks = mxUtils.bind(this, function (breaks) { + if (breaks != null) { + var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount; + + for (var i = 0; i <= count; i++) { + var pts = (breaks == this.horizontalPageBreaks) ? + [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)), + new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] : + [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)), + new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))]; + + if (breaks[i] != null) { + breaks[i].points = pts; + breaks[i].redraw(); + } + else { + var pageBreak = new mxPolyline(pts, this.pageBreakColor); + pageBreak.dialect = this.dialect; + pageBreak.isDashed = this.pageBreakDashed; + pageBreak.pointerEvents = false; + pageBreak.init(this.view.backgroundPane); + pageBreak.redraw(); + + breaks[i] = pageBreak; + } + } + + for (var i = count; i < breaks.length; i++) { + breaks[i].destroy(); + } + + breaks.splice(count, breaks.length - count); + } + }); + + drawPageBreaks(this.horizontalPageBreaks); + drawPageBreaks(this.verticalPageBreaks); + }; + + // Disables removing relative children from parents + var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent; + mxGraphHandler.prototype.shouldRemoveCellsFromParent = function (parent, cells, evt) { + for (var i = 0; i < cells.length; i++) { + if (this.graph.getModel().isVertex(cells[i])) { + var geo = this.graph.getCellGeometry(cells[i]); + + if (geo != null && geo.relative) { + return false; + } + } + } + + return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments); + }; + + // Overrides to ignore hotspot only for target terminal + var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; + mxConnectionHandler.prototype.createMarker = function () { + var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); + + marker.intersects = mxUtils.bind(this, function (state, evt) { + if (this.isConnecting()) { + return true; + } + + return mxCellMarker.prototype.intersects.apply(marker, arguments); + }); + + return marker; + }; + + // Creates background page shape + mxGraphView.prototype.createBackgroundPageShape = function (bounds) { + return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor); + }; + + // Fits the number of background pages to the graph + mxGraphView.prototype.getBackgroundPageBounds = function () { + var gb = this.getGraphBounds(); + + // Computes unscaled, untranslated graph bounds + var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0; + var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0; + var w = gb.width / this.scale; + var h = gb.height / this.scale; + + var fmt = this.graph.pageFormat; + var ps = this.graph.pageScale; + + var pw = fmt.width * ps; + var ph = fmt.height * ps; + + var x0 = Math.floor(Math.min(0, x) / pw); + var y0 = Math.floor(Math.min(0, y) / ph); + var xe = Math.ceil(Math.max(1, x + w) / pw); + var ye = Math.ceil(Math.max(1, y + h) / ph); + + var rows = xe - x0; + var cols = ye - y0; + + var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale * + (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph); + + return bounds; + }; + + // Add panning for background page in VML + var graphPanGraph = mxGraph.prototype.panGraph; + mxGraph.prototype.panGraph = function (dx, dy) { + graphPanGraph.apply(this, arguments); + + if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) && + (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container))) { + this.view.backgroundPageShape.node.style.marginLeft = dx + 'px'; + this.view.backgroundPageShape.node.style.marginTop = dy + 'px'; + } + }; + + /** + * Consumes click events for disabled menu items. + */ + var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem; + mxPopupMenu.prototype.addItem = function (title, image, funct, parent, iconCls, enabled) { + var result = mxPopupMenuAddItem.apply(this, arguments); + + if (enabled != null && !enabled) { + mxEvent.addListener(result, 'mousedown', function (evt) { + mxEvent.consume(evt); + }); + } + + return result; + }; + + // Selects ancestors before descendants + var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent; + mxGraphHandler.prototype.getInitialCellForEvent = function (me) { + var model = this.graph.getModel(); + var psel = model.getParent(this.graph.getSelectionCell()); + var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments); + var parent = model.getParent(cell); + + if (psel == null || (psel != cell && psel != parent)) { + while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) && + model.isVertex(parent) && !this.graph.isContainer(parent)) { + cell = parent; + parent = this.graph.getModel().getParent(cell); + } + } + + return cell; + }; + + // Selection is delayed to mouseup if ancestor is selected + var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection; + mxGraphHandler.prototype.isDelayedSelection = function (cell, me) { + var result = graphHandlerIsDelayedSelection.apply(this, arguments); + + if (!result) { + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (parent != null) { + // Inconsistency for unselected parent swimlane is intended for easier moving + // of stack layouts where the container title section is too far away + if (this.graph.isCellSelected(parent) && model.isVertex(parent)) { + result = true; + break; + } + + parent = model.getParent(parent); + } + } + + return result; + }; + + // Delayed selection of parent group + mxGraphHandler.prototype.selectDelayed = function (me) { + if (!this.graph.popupMenuHandler.isPopupTrigger(me)) { + var cell = me.getCell(); + + if (cell == null) { + cell = this.cell; + } + + // Selects folded cell for hit on folding icon + var state = this.graph.view.getState(cell) + + if (state != null && me.isSource(state.control)) { + this.graph.selectCellForEvent(cell, me.getEvent()); + } + else { + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (!this.graph.isCellSelected(parent) && model.isVertex(parent)) { + cell = parent; + parent = model.getParent(cell); + } + + this.graph.selectCellForEvent(cell, me.getEvent()); + } + } + }; + + // Returns last selected ancestor + mxPopupMenuHandler.prototype.getCellForPopupEvent = function (me) { + var cell = me.getCell(); + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (model.isVertex(parent) && !this.graph.isContainer(parent)) { + if (this.graph.isCellSelected(parent)) { + cell = parent; + } + + parent = model.getParent(parent); + } + + return cell; + }; + +})(); diff --git a/static/mxgraph/examples/grapheditor/www/js/EditorUi.js b/static/mxgraph/examples/grapheditor/www/js/EditorUi.js new file mode 100644 index 0000000..71d5ae1 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/EditorUi.js @@ -0,0 +1,4713 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new graph editor + */ +EditorUi = function(editor, container, lightbox) +{ + mxEventSource.call(this); + + this.destroyFunctions = []; + this.editor = editor || new Editor(); + this.container = container || document.body; + + var graph = this.editor.graph; + graph.lightbox = lightbox; + + // Faster scrollwheel zoom is possible with CSS transforms + if (graph.useCssTransforms) + { + this.lazyZoomDelay = 0; + } + + // Pre-fetches submenu image or replaces with embedded image if supported + if (mxClient.IS_SVG) + { + mxPopupMenu.prototype.submenuImage = 'data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs='; + } + else + { + new Image().src = mxPopupMenu.prototype.submenuImage; + } + + // Pre-fetches connect image + if (!mxClient.IS_SVG && mxConnectionHandler.prototype.connectImage != null) + { + new Image().src = mxConnectionHandler.prototype.connectImage.src; + } + + // Disables graph and forced panning in chromeless mode + if (this.editor.chromeless && !this.editor.editable) + { + this.footerHeight = 0; + graph.isEnabled = function() { return false; }; + graph.panningHandler.isForcePanningEvent = function(me) + { + return !mxEvent.isPopupTrigger(me.getEvent()); + }; + } + + // Creates the user interface + this.actions = new Actions(this); + this.menus = this.createMenus(); + + if (!graph.standalone) + { + this.createDivs(); + this.createUi(); + this.refresh(); + + // Disables HTML and text selection + var textEditing = mxUtils.bind(this, function(evt) + { + if (evt == null) + { + evt = window.event; + } + + return graph.isEditing() || (evt != null && this.isSelectionAllowed(evt)); + }); + + // Disables text selection while not editing and no dialog visible + if (this.container == document.body) + { + this.menubarContainer.onselectstart = textEditing; + this.menubarContainer.onmousedown = textEditing; + this.toolbarContainer.onselectstart = textEditing; + this.toolbarContainer.onmousedown = textEditing; + this.diagramContainer.onselectstart = textEditing; + this.diagramContainer.onmousedown = textEditing; + this.sidebarContainer.onselectstart = textEditing; + this.sidebarContainer.onmousedown = textEditing; + this.formatContainer.onselectstart = textEditing; + this.formatContainer.onmousedown = textEditing; + this.footerContainer.onselectstart = textEditing; + this.footerContainer.onmousedown = textEditing; + + if (this.tabContainer != null) + { + // Mouse down is needed for drag and drop + this.tabContainer.onselectstart = textEditing; + } + } + + // And uses built-in context menu while editing + if (!this.editor.chromeless || this.editor.editable) + { + // Allows context menu for links in hints + var linkHandler = function(evt) + { + if (evt != null) + { + var source = mxEvent.getSource(evt); + + if (source.nodeName == 'A') + { + while (source != null) + { + if (source.className == 'geHint') + { + return true; + } + + source = source.parentNode; + } + } + } + + return textEditing(evt); + }; + + if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) + { + mxEvent.addListener(this.diagramContainer, 'contextmenu', linkHandler); + } + else + { + // Allows browser context menu outside of diagram and sidebar + this.diagramContainer.oncontextmenu = linkHandler; + } + } + else + { + graph.panningHandler.usePopupTrigger = false; + } + + // Contains the main graph instance inside the given panel + graph.init(this.diagramContainer); + + // Improves line wrapping for in-place editor + if (mxClient.IS_SVG && graph.view.getDrawPane() != null) + { + var root = graph.view.getDrawPane().ownerSVGElement; + + if (root != null) + { + root.style.position = 'absolute'; + } + } + + // Creates hover icons + this.hoverIcons = this.createHoverIcons(); + + // Adds tooltip when mouse is over scrollbars to show space-drag panning option + mxEvent.addListener(this.diagramContainer, 'mousemove', mxUtils.bind(this, function(evt) + { + var off = mxUtils.getOffset(this.diagramContainer); + + if (mxEvent.getClientX(evt) - off.x - this.diagramContainer.clientWidth > 0 || + mxEvent.getClientY(evt) - off.y - this.diagramContainer.clientHeight > 0) + { + this.diagramContainer.setAttribute('title', mxResources.get('panTooltip')); + } + else + { + this.diagramContainer.removeAttribute('title'); + } + })); + + // Escape key hides dialogs, adds space+drag panning + var spaceKeyPressed = false; + + // Overrides hovericons to disable while space key is pressed + var hoverIconsIsResetEvent = this.hoverIcons.isResetEvent; + + this.hoverIcons.isResetEvent = function(evt, allowShift) + { + return spaceKeyPressed || hoverIconsIsResetEvent.apply(this, arguments); + }; + + this.keydownHandler = mxUtils.bind(this, function(evt) + { + if (evt.which == 32 /* Space */ && !graph.isEditing()) + { + spaceKeyPressed = true; + this.hoverIcons.reset(); + graph.container.style.cursor = 'move'; + + // Disables scroll after space keystroke with scrollbars + if (!graph.isEditing() && mxEvent.getSource(evt) == graph.container) + { + mxEvent.consume(evt); + } + } + else if (!mxEvent.isConsumed(evt) && evt.keyCode == 27 /* Escape */) + { + this.hideDialog(null, true); + } + }); + + mxEvent.addListener(document, 'keydown', this.keydownHandler); + + this.keyupHandler = mxUtils.bind(this, function(evt) + { + graph.container.style.cursor = ''; + spaceKeyPressed = false; + }); + + mxEvent.addListener(document, 'keyup', this.keyupHandler); + + // Forces panning for middle and right mouse buttons + var panningHandlerIsForcePanningEvent = graph.panningHandler.isForcePanningEvent; + graph.panningHandler.isForcePanningEvent = function(me) + { + // Ctrl+left button is reported as right button in FF on Mac + return panningHandlerIsForcePanningEvent.apply(this, arguments) || + spaceKeyPressed || (mxEvent.isMouseEvent(me.getEvent()) && + (this.usePopupTrigger || !mxEvent.isPopupTrigger(me.getEvent())) && + ((!mxEvent.isControlDown(me.getEvent()) && + mxEvent.isRightMouseButton(me.getEvent())) || + mxEvent.isMiddleMouseButton(me.getEvent()))); + }; + + // Ctrl/Cmd+Enter applies editing value except in Safari where Ctrl+Enter creates + // a new line (while Enter creates a new paragraph and Shift+Enter stops) + var cellEditorIsStopEditingEvent = graph.cellEditor.isStopEditingEvent; + graph.cellEditor.isStopEditingEvent = function(evt) + { + return cellEditorIsStopEditingEvent.apply(this, arguments) || + (evt.keyCode == 13 && ((!mxClient.IS_SF && mxEvent.isControlDown(evt)) || + (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || + (mxClient.IS_SF && mxEvent.isShiftDown(evt)))); + }; + + // Adds space+wheel for zoom + var graphIsZoomWheelEvent = graph.isZoomWheelEvent; + + graph.isZoomWheelEvent = function() + { + return spaceKeyPressed || graphIsZoomWheelEvent.apply(this, arguments); + }; + + // Switches toolbar for text editing + var textMode = false; + var fontMenu = null; + var sizeMenu = null; + var nodes = null; + + var updateToolbar = mxUtils.bind(this, function() + { + if (this.toolbar != null && textMode != graph.cellEditor.isContentEditing()) + { + var node = this.toolbar.container.firstChild; + var newNodes = []; + + while (node != null) + { + var tmp = node.nextSibling; + + if (mxUtils.indexOf(this.toolbar.staticElements, node) < 0) + { + node.parentNode.removeChild(node); + newNodes.push(node); + } + + node = tmp; + } + + // Saves references to special items + var tmp1 = this.toolbar.fontMenu; + var tmp2 = this.toolbar.sizeMenu; + + if (nodes == null) + { + this.toolbar.createTextToolbar(); + } + else + { + for (var i = 0; i < nodes.length; i++) + { + this.toolbar.container.appendChild(nodes[i]); + } + + // Restores references to special items + this.toolbar.fontMenu = fontMenu; + this.toolbar.sizeMenu = sizeMenu; + } + + textMode = graph.cellEditor.isContentEditing(); + fontMenu = tmp1; + sizeMenu = tmp2; + nodes = newNodes; + } + }); + + var ui = this; + + // Overrides cell editor to update toolbar + var cellEditorStartEditing = graph.cellEditor.startEditing; + graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + updateToolbar(); + + if (graph.cellEditor.isContentEditing()) + { + var updating = false; + + var updateCssHandler = function() + { + if (!updating) + { + updating = true; + + window.setTimeout(function() + { + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null) + { + var css = mxUtils.getCurrentStyle(node); + + if (css != null && ui.toolbar != null) + { + // Strips leading and trailing quotes + var ff = css.fontFamily; + + if (ff.charAt(0) == '\'') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '\'') + { + ff = ff.substring(0, ff.length - 1); + } + + ui.toolbar.setFontName(ff); + ui.toolbar.setFontSize(parseInt(css.fontSize)); + } + } + + updating = false; + }, 0); + } + }; + + mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) + mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); + updateCssHandler(); + } + }; + + // Updates toolbar and handles possible errors + var cellEditorStopEditing = graph.cellEditor.stopEditing; + graph.cellEditor.stopEditing = function(cell, trigger) + { + try + { + cellEditorStopEditing.apply(this, arguments); + updateToolbar(); + } + catch (e) + { + ui.handleError(e); + } + }; + + // Enables scrollbars and sets cursor style for the container + graph.container.setAttribute('tabindex', '0'); + graph.container.style.cursor = 'default'; + + // Workaround for page scroll if embedded via iframe + if (window.self === window.top && graph.container.parentNode != null) + { + try + { + graph.container.focus(); + } + catch (e) + { + // ignores error in old versions of IE + } + } + + // Keeps graph container focused on mouse down + var graphFireMouseEvent = graph.fireMouseEvent; + graph.fireMouseEvent = function(evtName, me, sender) + { + if (evtName == mxEvent.MOUSE_DOWN) + { + this.container.focus(); + } + + graphFireMouseEvent.apply(this, arguments); + }; + + // Configures automatic expand on mouseover + graph.popupMenuHandler.autoExpand = true; + + // Installs context menu + if (this.menus != null) + { + graph.popupMenuHandler.factoryMethod = mxUtils.bind(this, function(menu, cell, evt) + { + this.menus.createPopupMenu(menu, cell, evt); + }); + } + + // Hides context menu + mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt) + { + graph.popupMenuHandler.hideMenu(); + })); + + // Create handler for key events + this.keyHandler = this.createKeyHandler(editor); + + // Getter for key handler + this.getKeyHandler = function() + { + return keyHandler; + }; + + // Stores the current style and assigns it to new cells + var styles = ['rounded', 'shadow', 'glass', 'dashed', 'dashPattern', 'comic', 'labelBackgroundColor']; + var connectStyles = ['shape', 'edgeStyle', 'curved', 'rounded', 'elbow', 'comic', 'jumpStyle', 'jumpSize']; + + // Note: Everything that is not in styles is ignored (styles is augmented below) + this.setDefaultStyle = function(cell) + { + try + { + var state = graph.view.getState(cell); + + if (state != null) + { + // Ignores default styles + var clone = cell.clone(); + clone.style = '' + var defaultStyle = graph.getCellStyle(clone); + var values = []; + var keys = []; + + for (var key in state.style) + { + if (defaultStyle[key] != state.style[key]) + { + values.push(state.style[key]); + keys.push(key); + } + } + + // Handles special case for value "none" + var cellStyle = graph.getModel().getStyle(state.cell); + var tokens = (cellStyle != null) ? cellStyle.split(';') : []; + + for (var i = 0; i < tokens.length; i++) + { + var tmp = tokens[i]; + var pos = tmp.indexOf('='); + + if (pos >= 0) + { + var key = tmp.substring(0, pos); + var value = tmp.substring(pos + 1); + + if (defaultStyle[key] != null && value == 'none') + { + values.push(value); + keys.push(key); + } + } + } + + // Resets current style + if (graph.getModel().isEdge(state.cell)) + { + graph.currentEdgeStyle = {}; + } + else + { + graph.currentVertexStyle = {} + } + + this.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values, 'cells', [state.cell])); + } + } + catch (e) + { + this.handleError(e); + } + }; + + this.clearDefaultStyle = function() + { + graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle); + graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle); + + // Updates UI + this.fireEvent(new mxEventObject('styleChanged', 'keys', [], 'values', [], 'cells', [])); + }; + + this.UPDATE_LOGS = function (div) { + if (this.editor.errorlogs_array) { + $("#errorlogs_list > p").remove() + this.editor.errorlogs_array + .forEach(msg => { + // mxUtils.para(div, msg); + $(div).append('

' + msg + '

') + }) + } + } + + // Keys that should be ignored if the cell has a value (known: new default for all cells is html=1 so + // for the html key this effecticely only works for edges inserted via the connection handler) + var valueStyles = ['fontFamily', 'fontSize', 'fontColor']; + + // Keys that always update the current edge style regardless of selection + var alwaysEdgeStyles = ['edgeStyle', 'startArrow', 'startFill', 'startSize', 'endArrow', + 'endFill', 'endSize']; + + // Keys that are ignored together (if one appears all are ignored) + var keyGroups = [['startArrow', 'startFill', 'startSize', 'sourcePerimeterSpacing', + 'endArrow', 'endFill', 'endSize', 'targetPerimeterSpacing'], + ['strokeColor', 'strokeWidth'], + ['fillColor', 'gradientColor'], + valueStyles, + ['opacity'], + ['align'], + ['html']]; + + // Adds all keys used above to the styles array + for (var i = 0; i < keyGroups.length; i++) + { + for (var j = 0; j < keyGroups[i].length; j++) + { + styles.push(keyGroups[i][j]); + } + } + + for (var i = 0; i < connectStyles.length; i++) + { + if (mxUtils.indexOf(styles, connectStyles[i]) < 0) + { + styles.push(connectStyles[i]); + } + } + + // Implements a global current style for edges and vertices that is applied to new cells + var insertHandler = function(cells, asText) + { + var model = graph.getModel(); + + model.beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + var appliedStyles; + + if (asText) + { + // Applies only basic text styles + appliedStyles = ['fontSize', 'fontFamily', 'fontColor']; + } + else + { + // Removes styles defined in the cell style from the styles to be applied + var cellStyle = model.getStyle(cell); + var tokens = (cellStyle != null) ? cellStyle.split(';') : []; + appliedStyles = styles.slice(); + + for (var j = 0; j < tokens.length; j++) + { + var tmp = tokens[j]; + var pos = tmp.indexOf('='); + + if (pos >= 0) + { + var key = tmp.substring(0, pos); + var index = mxUtils.indexOf(appliedStyles, key); + + if (index >= 0) + { + appliedStyles.splice(index, 1); + } + + // Handles special cases where one defined style ignores other styles + for (var k = 0; k < keyGroups.length; k++) + { + var group = keyGroups[k]; + + if (mxUtils.indexOf(group, key) >= 0) + { + for (var l = 0; l < group.length; l++) + { + var index2 = mxUtils.indexOf(appliedStyles, group[l]); + + if (index2 >= 0) + { + appliedStyles.splice(index2, 1); + } + } + } + } + } + } + } + + // Applies the current style to the cell + var edge = model.isEdge(cell); + var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle; + var newStyle = model.getStyle(cell); + + for (var j = 0; j < appliedStyles.length; j++) + { + var key = appliedStyles[j]; + var styleValue = current[key]; + + if (styleValue != null && (key != 'shape' || edge)) + { + // Special case: Connect styles are not applied here but in the connection handler + if (!edge || mxUtils.indexOf(connectStyles, key) < 0) + { + newStyle = mxUtils.setStyle(newStyle, key, styleValue); + } + } + } + + model.setStyle(cell, newStyle); + } + } + finally + { + model.endUpdate(); + } + }; + + graph.addListener('cellsInserted', function(sender, evt) + { + insertHandler(evt.getProperty('cells')); + }); + + graph.addListener('textInserted', function(sender, evt) + { + insertHandler(evt.getProperty('cells'), true); + }); + + graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt) + { + var cells = [evt.getProperty('cell')]; + + if (evt.getProperty('terminalInserted')) + { + cells.push(evt.getProperty('terminal')); + } + + insertHandler(cells); + }); + + this.addListener('styleChanged', mxUtils.bind(this, function(sender, evt) + { + // Checks if edges and/or vertices were modified + var cells = evt.getProperty('cells'); + var vertex = false; + var edge = false; + + if (cells.length > 0) + { + for (var i = 0; i < cells.length; i++) + { + vertex = graph.getModel().isVertex(cells[i]) || vertex; + edge = graph.getModel().isEdge(cells[i]) || edge; + + if (edge && vertex) + { + break; + } + } + } + else + { + vertex = true; + edge = true; + } + + var keys = evt.getProperty('keys'); + var values = evt.getProperty('values'); + + for (var i = 0; i < keys.length; i++) + { + var common = mxUtils.indexOf(valueStyles, keys[i]) >= 0; + + // Ignores transparent stroke colors + if (keys[i] != 'strokeColor' || (values[i] != null && values[i] != 'none')) + { + // Special case: Edge style and shape + if (mxUtils.indexOf(connectStyles, keys[i]) >= 0) + { + if (edge || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentEdgeStyle[keys[i]]; + } + else + { + graph.currentEdgeStyle[keys[i]] = values[i]; + } + } + // Uses style for vertex if defined in styles + else if (vertex && mxUtils.indexOf(styles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentVertexStyle[keys[i]]; + } + else + { + graph.currentVertexStyle[keys[i]] = values[i]; + } + } + } + else if (mxUtils.indexOf(styles, keys[i]) >= 0) + { + if (vertex || common) + { + if (values[i] == null) + { + delete graph.currentVertexStyle[keys[i]]; + } + else + { + graph.currentVertexStyle[keys[i]] = values[i]; + } + } + + if (edge || common || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentEdgeStyle[keys[i]]; + } + else + { + graph.currentEdgeStyle[keys[i]] = values[i]; + } + } + } + } + } + + if (this.toolbar != null) + { + this.toolbar.setFontName(graph.currentVertexStyle['fontFamily'] || Menus.prototype.defaultFont); + this.toolbar.setFontSize(graph.currentVertexStyle['fontSize'] || Menus.prototype.defaultFontSize); + + if (this.toolbar.edgeStyleMenu != null) + { + // Updates toolbar icon for edge style + var edgeStyleDiv = this.toolbar.edgeStyleMenu.getElementsByTagName('div')[0]; + + if (graph.currentEdgeStyle['edgeStyle'] == 'orthogonalEdgeStyle' && graph.currentEdgeStyle['curved'] == '1') + { + edgeStyleDiv.className = 'geSprite geSprite-curved'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'straight' || graph.currentEdgeStyle['edgeStyle'] == 'none' || + graph.currentEdgeStyle['edgeStyle'] == null) + { + edgeStyleDiv.className = 'geSprite geSprite-straight'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'entityRelationEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-entity'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? + 'verticalelbow' : 'horizontalelbow'); + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'isometricEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? + 'verticalisometric' : 'horizontalisometric'); + } + else + { + edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; + } + } + + if (this.toolbar.edgeShapeMenu != null) + { + // Updates icon for edge shape + var edgeShapeDiv = this.toolbar.edgeShapeMenu.getElementsByTagName('div')[0]; + + if (graph.currentEdgeStyle['shape'] == 'link') + { + edgeShapeDiv.className = 'geSprite geSprite-linkedge'; + } + else if (graph.currentEdgeStyle['shape'] == 'flexArrow') + { + edgeShapeDiv.className = 'geSprite geSprite-arrow'; + } + else if (graph.currentEdgeStyle['shape'] == 'arrow') + { + edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; + } + else + { + edgeShapeDiv.className = 'geSprite geSprite-connection'; + } + } + + // Updates icon for optinal line start shape + if (this.toolbar.lineStartMenu != null) + { + var lineStartDiv = this.toolbar.lineStartMenu.getElementsByTagName('div')[0]; + + lineStartDiv.className = this.getCssClassForMarker('start', + graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_STARTARROW], + mxUtils.getValue(graph.currentEdgeStyle, 'startFill', '1')); + } + + // Updates icon for optinal line end shape + if (this.toolbar.lineEndMenu != null) + { + var lineEndDiv = this.toolbar.lineEndMenu.getElementsByTagName('div')[0]; + + lineEndDiv.className = this.getCssClassForMarker('end', + graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_ENDARROW], + mxUtils.getValue(graph.currentEdgeStyle, 'endFill', '1')); + } + } + })); + + // Update font size and font family labels + if (this.toolbar != null) + { + var update = mxUtils.bind(this, function() + { + var ff = graph.currentVertexStyle['fontFamily'] || 'Helvetica'; + var fs = String(graph.currentVertexStyle['fontSize'] || '12'); + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + ff = state.style[mxConstants.STYLE_FONTFAMILY] || ff; + fs = state.style[mxConstants.STYLE_FONTSIZE] || fs; + + if (ff.length > 10) + { + ff = ff.substring(0, 8) + '...'; + } + } + + this.toolbar.setFontName(ff); + this.toolbar.setFontSize(fs); + }); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, update); + graph.getModel().addListener(mxEvent.CHANGE, update); + } + + // Makes sure the current layer is visible when cells are added + graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) + { + var cells = evt.getProperty('cells'); + var parent = evt.getProperty('parent'); + + if (graph.getModel().isLayer(parent) && !graph.isCellVisible(parent) && cells != null && cells.length > 0) + { + graph.getModel().setVisible(parent, true); + } + }); + + // Global handler to hide the current menu + this.gestureHandler = mxUtils.bind(this, function(evt) + { + if (this.currentMenu != null && mxEvent.getSource(evt) != this.currentMenu.div) + { + this.hideCurrentMenu(); + } + }); + + mxEvent.addGestureListeners(document, this.gestureHandler); + + // Updates the editor UI after the window has been resized or the orientation changes + // Timeout is workaround for old IE versions which have a delay for DOM client sizes. + // Should not use delay > 0 to avoid handle multiple repaints during window resize + this.resizeHandler = mxUtils.bind(this, function() + { + window.setTimeout(mxUtils.bind(this, function() + { + if (this.editor.graph != null) + { + this.refresh(); + } + }), 0); + }); + + mxEvent.addListener(window, 'resize', this.resizeHandler); + + this.orientationChangeHandler = mxUtils.bind(this, function() + { + this.refresh(); + }); + + mxEvent.addListener(window, 'orientationchange', this.orientationChangeHandler); + + // Workaround for bug on iOS see + // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue + if (mxClient.IS_IOS && !window.navigator.standalone) + { + this.scrollHandler = mxUtils.bind(this, function() + { + window.scrollTo(0, 0); + }); + + mxEvent.addListener(window, 'scroll', this.scrollHandler); + } + + /** + * Sets the initial scrollbar locations after a file was loaded. + */ + this.editor.addListener('resetGraphView', mxUtils.bind(this, function() + { + this.resetScrollbars(); + })); + + /** + * Repaints the grid. + */ + this.addListener('gridEnabledChanged', mxUtils.bind(this, function() + { + graph.view.validateBackground(); + })); + + this.addListener('backgroundColorChanged', mxUtils.bind(this, function() + { + graph.view.validateBackground(); + })); + + /** + * Repaints the grid. + */ + graph.addListener('gridSizeChanged', mxUtils.bind(this, function() + { + if (graph.isGridEnabled()) + { + graph.view.validateBackground(); + } + })); + + // Resets UI, updates action and menu states + this.editor.resetGraph(); + } + + this.init(); + + if (!graph.standalone) + { + this.open(); + } + return this; +}; + +// Extends mxEventSource +mxUtils.extend(EditorUi, mxEventSource); + +/** + * Global config that specifies if the compact UI elements should be used. + */ +EditorUi.compactUi = true; + +/** + * Specifies the size of the split bar. + */ +EditorUi.prototype.splitSize = (mxClient.IS_TOUCH || mxClient.IS_POINTER) ? 12 : 8; + +/** + * Specifies the height of the menubar. Default is 30. + */ +EditorUi.prototype.menubarHeight = 30; + +/** + * Specifies the width of the format panel should be enabled. Default is true. + */ +EditorUi.prototype.formatEnabled = true; + +/** + * Specifies the width of the format panel. Default is 240. + */ +EditorUi.prototype.formatWidth = 240; + +/** + * Specifies the height of the toolbar. Default is 38. + */ +EditorUi.prototype.toolbarHeight = 38; + +/** + * Specifies the height of the footer. Default is 28. + */ +EditorUi.prototype.footerHeight = 28; + +/** + * Specifies the height of the optional sidebarFooterContainer. Default is 34. + */ +EditorUi.prototype.sidebarFooterHeight = 34; + +/** + * Specifies the position of the horizontal split bar. Default is 240 or 118 for + * screen widths <= 640px. + */ +EditorUi.prototype.hsplitPosition = (screen.width <= 640) ? 118 : ((urlParams['sidebar-entries'] != 'large') ? 212 : 240); + +/** + * Specifies if animations are allowed in . Default is true. + */ +EditorUi.prototype.allowAnimation = true; + +/** + * Default is 2. + */ +EditorUi.prototype.lightboxMaxFitScale = 2; + +/** + * Default is 4. + */ +EditorUi.prototype.lightboxVerticalDivider = 4; + +/** + * Specifies if single click on horizontal split should collapse sidebar. Default is false. + */ +EditorUi.prototype.hsplitClickEnabled = false; + +/** + * Installs the listeners to update the action states. + */ +EditorUi.prototype.init = function() +{ + var graph = this.editor.graph; + + if (!graph.standalone) + { + // Hides tooltips and connection points when scrolling + mxEvent.addListener(graph.container, 'scroll', mxUtils.bind(this, function() + { + graph.tooltipHandler.hide(); + + if (graph.connectionHandler != null && graph.connectionHandler.constraintHandler != null) + { + graph.connectionHandler.constraintHandler.reset(); + } + })); + + // Hides tooltip on escape + graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function() + { + graph.tooltipHandler.hide(); + var rb = graph.getRubberband(); + + if (rb != null) + { + rb.cancel(); + } + })); + + mxEvent.addListener(graph.container, 'keydown', mxUtils.bind(this, function(evt) + { + this.onKeyDown(evt); + })); + + mxEvent.addListener(graph.container, 'keypress', mxUtils.bind(this, function(evt) + { + this.onKeyPress(evt); + })); + + // Updates action states + this.addUndoListener(); + this.addBeforeUnloadListener(); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + this.updateActionStates(); + })); + + graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + this.updateActionStates(); + })); + + // Changes action states after change of default parent + var graphSetDefaultParent = graph.setDefaultParent; + var ui = this; + + this.editor.graph.setDefaultParent = function() + { + graphSetDefaultParent.apply(this, arguments); + ui.updateActionStates(); + }; + + // Hack to make editLink available in vertex handler + graph.editLink = ui.actions.get('editLink').funct; + + this.updateActionStates(); + this.initClipboard(); + this.initCanvas(); + + if (this.format != null) + { + this.format.init(); + } + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.onKeyDown = function(evt) +{ + var graph = this.editor.graph; + + // Tab selects next cell + if (evt.which == 9 && graph.isEnabled() && !mxEvent.isAltDown(evt) && + (!graph.isEditing() || !mxEvent.isShiftDown(evt))) + { + if (graph.isEditing()) + { + graph.stopEditing(false); + } + else + { + graph.selectCell(!mxEvent.isShiftDown(evt)); + } + + mxEvent.consume(evt); + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.onKeyPress = function(evt) +{ + var graph = this.editor.graph; + + // KNOWN: Focus does not work if label is empty in quirks mode + if (this.isImmediateEditingEvent(evt) && !graph.isEditing() && !graph.isSelectionEmpty() && evt.which !== 0 && + evt.which !== 27 && !mxEvent.isAltDown(evt) && !mxEvent.isControlDown(evt) && !mxEvent.isMetaDown(evt)) + { + graph.escape(); + graph.startEditing(); + + // Workaround for FF where char is lost if cursor is placed before char + if (mxClient.IS_FF) + { + var ce = graph.cellEditor; + ce.textarea.innerHTML = String.fromCharCode(evt.which); + + // Moves cursor to end of textarea + var range = document.createRange(); + range.selectNodeContents(ce.textarea); + range.collapse(false); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.isImmediateEditingEvent = function(evt) +{ + return true; +}; + +/** + * Private helper method. + */ +EditorUi.prototype.getCssClassForMarker = function(prefix, shape, marker, fill) +{ + var result = ''; + + if (shape == 'flexArrow') + { + result = (marker != null && marker != mxConstants.NONE) ? + 'geSprite geSprite-' + prefix + 'blocktrans' : 'geSprite geSprite-noarrow'; + } + else + { + // SVG marker sprites + if (marker == 'box' || marker == 'halfCircle') + { + result = 'geSprite geSvgSprite geSprite-' + marker + ((prefix == 'end') ? ' geFlipSprite' : ''); + } + else if (marker == mxConstants.ARROW_CLASSIC) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classic' : 'geSprite geSprite-' + prefix + 'classictrans'; + } + else if (marker == mxConstants.ARROW_CLASSIC_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classicthin' : 'geSprite geSprite-' + prefix + 'classicthintrans'; + } + else if (marker == mxConstants.ARROW_OPEN) + { + result = 'geSprite geSprite-' + prefix + 'open'; + } + else if (marker == mxConstants.ARROW_OPEN_THIN) + { + result = 'geSprite geSprite-' + prefix + 'openthin'; + } + else if (marker == mxConstants.ARROW_BLOCK) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'block' : 'geSprite geSprite-' + prefix + 'blocktrans'; + } + else if (marker == mxConstants.ARROW_BLOCK_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'blockthin' : 'geSprite geSprite-' + prefix + 'blockthintrans'; + } + else if (marker == mxConstants.ARROW_OVAL) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'oval' : 'geSprite geSprite-' + prefix + 'ovaltrans'; + } + else if (marker == mxConstants.ARROW_DIAMOND) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'diamond' : 'geSprite geSprite-' + prefix + 'diamondtrans'; + } + else if (marker == mxConstants.ARROW_DIAMOND_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'thindiamond' : 'geSprite geSprite-' + prefix + 'thindiamondtrans'; + } + else if (marker == 'openAsync') + { + result = 'geSprite geSprite-' + prefix + 'openasync'; + } + else if (marker == 'dash') + { + result = 'geSprite geSprite-' + prefix + 'dash'; + } + else if (marker == 'cross') + { + result = 'geSprite geSprite-' + prefix + 'cross'; + } + else if (marker == 'async') + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'async' : 'geSprite geSprite-' + prefix + 'asynctrans'; + } + else if (marker == 'circle' || marker == 'circlePlus') + { + result = (fill == '1' || marker == 'circle') ? 'geSprite geSprite-' + prefix + 'circle' : 'geSprite geSprite-' + prefix + 'circleplus'; + } + else if (marker == 'ERone') + { + result = 'geSprite geSprite-' + prefix + 'erone'; + } + else if (marker == 'ERmandOne') + { + result = 'geSprite geSprite-' + prefix + 'eronetoone'; + } + else if (marker == 'ERmany') + { + result = 'geSprite geSprite-' + prefix + 'ermany'; + } + else if (marker == 'ERoneToMany') + { + result = 'geSprite geSprite-' + prefix + 'eronetomany'; + } + else if (marker == 'ERzeroToOne') + { + result = 'geSprite geSprite-' + prefix + 'eroneopt'; + } + else if (marker == 'ERzeroToMany') + { + result = 'geSprite geSprite-' + prefix + 'ermanyopt'; + } + else + { + result = 'geSprite geSprite-noarrow'; + } + } + + return result; +}; + +/** + * Overridden in Menus.js + */ +EditorUi.prototype.createMenus = function() +{ + return null; +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.updatePasteActionStates = function() +{ + var graph = this.editor.graph; + var paste = this.actions.get('paste'); + var pasteHere = this.actions.get('pasteHere'); + + paste.setEnabled(this.editor.graph.cellEditor.isContentEditing() || (!mxClipboard.isEmpty() && + graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))); + pasteHere.setEnabled(paste.isEnabled()); +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.initClipboard = function() +{ + var ui = this; + + var mxClipboardCut = mxClipboard.cut; + mxClipboard.cut = function(graph) + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('cut', false, null); + } + else + { + mxClipboardCut.apply(this, arguments); + } + + ui.updatePasteActionStates(); + }; + + var mxClipboardCopy = mxClipboard.copy; + mxClipboard.copy = function(graph) + { + var result = null; + + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('copy', false, null); + } + else + { + result = result || graph.getSelectionCells(); + result = graph.getExportableCells(graph.model.getTopmostCells(result)); + + var cloneMap = new Object(); + var lookup = graph.createCellLookup(result); + var clones = graph.cloneCells(result, null, cloneMap); + + // Uses temporary model to force new IDs to be assigned + // to avoid having to carry over the mapping from object + // ID to cell ID to the paste operation + var model = new mxGraphModel(); + var parent = model.getChildAt(model.getRoot(), 0); + + for (var i = 0; i < clones.length; i++) + { + model.add(parent, clones[i]); + } + + graph.updateCustomLinks(graph.createCellMapping(cloneMap, lookup), clones); + + mxClipboard.insertCount = 1; + mxClipboard.setCells(clones); + } + + ui.updatePasteActionStates(); + + return result; + }; + + var mxClipboardPaste = mxClipboard.paste; + mxClipboard.paste = function(graph) + { + var result = null; + + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('paste', false, null); + } + else + { + result = mxClipboardPaste.apply(this, arguments); + } + + ui.updatePasteActionStates(); + + return result; + }; + + // Overrides cell editor to update paste action state + var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; + + this.editor.graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + ui.updatePasteActionStates(); + }; + + var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; + + this.editor.graph.cellEditor.stopEditing = function(cell, trigger) + { + cellEditorStopEditing.apply(this, arguments); + ui.updatePasteActionStates(); + }; + + this.updatePasteActionStates(); +}; + +/** + * Delay between zoom steps when not using preview. + */ +EditorUi.prototype.lazyZoomDelay = 20; + +/** + * Delay before update of DOM when using preview. + */ +EditorUi.prototype.wheelZoomDelay = 400; + +/** + * Delay before update of DOM when using preview. + */ +EditorUi.prototype.buttonZoomDelay = 600; + +/** + * Initializes the infinite canvas. + */ +EditorUi.prototype.initCanvas = function() +{ + // Initial page layout view, scrollBuffer and timer-based scrolling + var graph = this.editor.graph; + graph.timerAutoScroll = true; + + /** + * Returns the padding for pages in page view with scrollbars. + */ + graph.getPagePadding = function() + { + return new mxPoint(Math.max(0, Math.round((graph.container.offsetWidth - 34) / graph.view.scale)), + Math.max(0, Math.round((graph.container.offsetHeight - 34) / graph.view.scale))); + }; + + // Fits the number of background pages to the graph + graph.view.getBackgroundPageBounds = function() + { + var layout = this.graph.getPageLayout(); + var page = this.graph.getPageSize(); + + return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width), + this.scale * (this.translate.y + layout.y * page.height), + this.scale * layout.width * page.width, + this.scale * layout.height * page.height); + }; + + graph.getPreferredPageSize = function(bounds, width, height) + { + var pages = this.getPageLayout(); + var size = this.getPageSize(); + + return new mxRectangle(0, 0, pages.width * size.width, pages.height * size.height); + }; + + // Scales pages/graph to fit available size + var resize = null; + var ui = this; + + if (this.editor.isChromelessView()) + { + resize = mxUtils.bind(this, function(autoscale, maxScale, cx, cy) + { + if (graph.container != null && !graph.isViewer()) + { + cx = (cx != null) ? cx : 0; + cy = (cy != null) ? cy : 0; + + var bds = (graph.pageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds(); + var scroll = mxUtils.hasScrollbars(graph.container); + var tr = graph.view.translate; + var s = graph.view.scale; + + // Normalizes the bounds + var b = mxRectangle.fromRectangle(bds); + b.x = b.x / s - tr.x; + b.y = b.y / s - tr.y; + b.width /= s; + b.height /= s; + + var st = graph.container.scrollTop; + var sl = graph.container.scrollLeft; + var sb = (mxClient.IS_QUIRKS || document.documentMode >= 8) ? 20 : 14; + + if (document.documentMode == 8 || document.documentMode == 9) + { + sb += 3; + } + + var cw = graph.container.offsetWidth - sb; + var ch = graph.container.offsetHeight - sb; + + var ns = (autoscale) ? Math.max(0.3, Math.min(maxScale || 1, cw / b.width)) : s; + var dx = ((cw - ns * b.width) / 2) / ns; + var dy = (this.lightboxVerticalDivider == 0) ? 0 : ((ch - ns * b.height) / this.lightboxVerticalDivider) / ns; + + if (scroll) + { + dx = Math.max(dx, 0); + dy = Math.max(dy, 0); + } + + if (scroll || bds.width < cw || bds.height < ch) + { + graph.view.scaleAndTranslate(ns, Math.floor(dx - b.x), Math.floor(dy - b.y)); + graph.container.scrollTop = st * ns / s; + graph.container.scrollLeft = sl * ns / s; + } + else if (cx != 0 || cy != 0) + { + var t = graph.view.translate; + graph.view.setTranslate(Math.floor(t.x + cx / s), Math.floor(t.y + cy / s)); + } + } + }); + + // Hack to make function available to subclassers + this.chromelessResize = resize; + + // Hook for subclassers for override + this.chromelessWindowResize = mxUtils.bind(this, function() + { + this.chromelessResize(false); + }); + + // Removable resize listener + var autoscaleResize = mxUtils.bind(this, function() + { + this.chromelessWindowResize(false); + }); + + mxEvent.addListener(window, 'resize', autoscaleResize); + + this.destroyFunctions.push(function() + { + mxEvent.removeListener(window, 'resize', autoscaleResize); + }); + + this.editor.addListener('resetGraphView', mxUtils.bind(this, function() + { + this.chromelessResize(true); + })); + + this.actions.get('zoomIn').funct = mxUtils.bind(this, function(evt) + { + graph.zoomIn(); + this.chromelessResize(false); + }); + this.actions.get('zoomOut').funct = mxUtils.bind(this, function(evt) + { + graph.zoomOut(); + this.chromelessResize(false); + }); + + // Creates toolbar for viewer - do not use CSS here + // as this may be used in a viewer that has no CSS + if (urlParams['toolbar'] != '0') + { + var toolbarConfig = JSON.parse(decodeURIComponent(urlParams['toolbar-config'] || '{}')); + + this.chromelessToolbar = document.createElement('div'); + this.chromelessToolbar.style.position = 'fixed'; + this.chromelessToolbar.style.overflow = 'hidden'; + this.chromelessToolbar.style.boxSizing = 'border-box'; + this.chromelessToolbar.style.whiteSpace = 'nowrap'; + this.chromelessToolbar.style.backgroundColor = '#000000'; + this.chromelessToolbar.style.padding = '10px 10px 8px 10px'; + this.chromelessToolbar.style.left = (graph.isViewer()) ? '0' : '50%'; + + if (!mxClient.IS_VML) + { + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'borderRadius', '20px'); + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transition', 'opacity 600ms ease-in-out'); + } + + var updateChromelessToolbarPosition = mxUtils.bind(this, function() + { + var css = mxUtils.getCurrentStyle(graph.container); + + if (graph.isViewer()) + { + this.chromelessToolbar.style.top = '0'; + } + else + { + this.chromelessToolbar.style.bottom = ((css != null) ? parseInt(css['margin-bottom'] || 0) : 0) + + ((this.tabContainer != null) ? (20 + parseInt(this.tabContainer.style.height)) : 20) + 'px'; + } + }); + + this.editor.addListener('resetGraphView', updateChromelessToolbarPosition); + updateChromelessToolbarPosition(); + + var btnCount = 0; + + var addButton = mxUtils.bind(this, function(fn, imgSrc, tip) + { + btnCount++; + + var a = document.createElement('span'); + a.style.paddingLeft = '8px'; + a.style.paddingRight = '8px'; + a.style.cursor = 'pointer'; + mxEvent.addListener(a, 'click', fn); + + if (tip != null) + { + a.setAttribute('title', tip); + } + + var img = document.createElement('img'); + img.setAttribute('border', '0'); + img.setAttribute('src', imgSrc); + + a.appendChild(img); + this.chromelessToolbar.appendChild(a); + + return a; + }); + + if (toolbarConfig.backBtn != null) + { + addButton(mxUtils.bind(this, function(evt) + { + window.location.href = toolbarConfig.backBtn.url; + mxEvent.consume(evt); + }), Editor.backLargeImage, mxResources.get('back', null, 'Back')); + } + + var prevButton = addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('previousPage').funct(); + mxEvent.consume(evt); + }), Editor.previousLargeImage, mxResources.get('previousPage')); + + var pageInfo = document.createElement('div'); + pageInfo.style.display = 'inline-block'; + pageInfo.style.verticalAlign = 'top'; + pageInfo.style.fontFamily = 'Helvetica,Arial'; + pageInfo.style.marginTop = '8px'; + pageInfo.style.fontSize = '14px'; + pageInfo.style.color = '#ffffff'; + this.chromelessToolbar.appendChild(pageInfo); + + var nextButton = addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('nextPage').funct(); + mxEvent.consume(evt); + }), Editor.nextLargeImage, mxResources.get('nextPage')); + + var updatePageInfo = mxUtils.bind(this, function() + { + if (this.pages != null && this.pages.length > 1 && this.currentPage != null) + { + pageInfo.innerHTML = ''; + mxUtils.write(pageInfo, (mxUtils.indexOf(this.pages, this.currentPage) + 1) + ' / ' + this.pages.length); + } + }); + + prevButton.style.paddingLeft = '0px'; + prevButton.style.paddingRight = '4px'; + nextButton.style.paddingLeft = '4px'; + nextButton.style.paddingRight = '0px'; + + var updatePageButtons = mxUtils.bind(this, function() + { + if (this.pages != null && this.pages.length > 1 && this.currentPage != null) + { + nextButton.style.display = ''; + prevButton.style.display = ''; + pageInfo.style.display = 'inline-block'; + } + else + { + nextButton.style.display = 'none'; + prevButton.style.display = 'none'; + pageInfo.style.display = 'none'; + } + + updatePageInfo(); + }); + + this.editor.addListener('resetGraphView', updatePageButtons); + this.editor.addListener('pageSelected', updatePageInfo); + + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('zoomOut').funct(); + mxEvent.consume(evt); + }), Editor.zoomOutLargeImage, mxResources.get('zoomOut') + ' (Alt+Mousewheel)'); + + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('zoomIn').funct(); + mxEvent.consume(evt); + }), Editor.zoomInLargeImage, mxResources.get('zoomIn') + ' (Alt+Mousewheel)'); + + addButton(mxUtils.bind(this, function(evt) + { + if (graph.isLightboxView()) + { + if (graph.view.scale == 1) + { + this.lightboxFit(); + } + else + { + graph.zoomTo(1); + } + + this.chromelessResize(false); + } + else + { + this.chromelessResize(true); + } + + mxEvent.consume(evt); + }), Editor.actualSizeLargeImage, mxResources.get('fit')); + + // Changes toolbar opacity on hover + var fadeThread = null; + var fadeThread2 = null; + + var fadeOut = mxUtils.bind(this, function(delay) + { + if (fadeThread != null) + { + window.clearTimeout(fadeThread); + fadeThead = null; + } + + if (fadeThread2 != null) + { + window.clearTimeout(fadeThread2); + fadeThead2 = null; + } + + fadeThread = window.setTimeout(mxUtils.bind(this, function() + { + mxUtils.setOpacity(this.chromelessToolbar, 0); + fadeThread = null; + + fadeThread2 = window.setTimeout(mxUtils.bind(this, function() + { + this.chromelessToolbar.style.display = 'none'; + fadeThread2 = null; + }), 600); + }), delay || 200); + }); + + var fadeIn = mxUtils.bind(this, function(opacity) + { + if (fadeThread != null) + { + window.clearTimeout(fadeThread); + fadeThead = null; + } + + if (fadeThread2 != null) + { + window.clearTimeout(fadeThread2); + fadeThead2 = null; + } + + this.chromelessToolbar.style.display = ''; + mxUtils.setOpacity(this.chromelessToolbar, opacity || 30); + }); + + if (urlParams['layers'] == '1') + { + this.layersDialog = null; + + var layersButton = addButton(mxUtils.bind(this, function(evt) + { + if (this.layersDialog != null) + { + this.layersDialog.parentNode.removeChild(this.layersDialog); + this.layersDialog = null; + } + else + { + this.layersDialog = graph.createLayersDialog(); + + mxEvent.addListener(this.layersDialog, 'mouseleave', mxUtils.bind(this, function() + { + this.layersDialog.parentNode.removeChild(this.layersDialog); + this.layersDialog = null; + })); + + var r = layersButton.getBoundingClientRect(); + + mxUtils.setPrefixedStyle(this.layersDialog.style, 'borderRadius', '5px'); + this.layersDialog.style.position = 'fixed'; + this.layersDialog.style.fontFamily = 'Helvetica,Arial'; + this.layersDialog.style.backgroundColor = '#000000'; + this.layersDialog.style.width = '160px'; + this.layersDialog.style.padding = '4px 2px 4px 2px'; + this.layersDialog.style.color = '#ffffff'; + mxUtils.setOpacity(this.layersDialog, 70); + this.layersDialog.style.left = r.left + 'px'; + this.layersDialog.style.bottom = parseInt(this.chromelessToolbar.style.bottom) + + this.chromelessToolbar.offsetHeight + 4 + 'px'; + + // Puts the dialog on top of the container z-index + var style = mxUtils.getCurrentStyle(this.editor.graph.container); + this.layersDialog.style.zIndex = style.zIndex; + + document.body.appendChild(this.layersDialog); + } + + mxEvent.consume(evt); + }), Editor.layersLargeImage, mxResources.get('layers')); + + // Shows/hides layers button depending on content + var model = graph.getModel(); + + model.addListener(mxEvent.CHANGE, function() + { + layersButton.style.display = (model.getChildCount(model.root) > 1) ? '' : 'none'; + }); + } + + this.addChromelessToolbarItems(addButton); + + if (this.editor.editButtonLink != null || this.editor.editButtonFunc != null) + { + addButton(mxUtils.bind(this, function(evt) + { + if (this.editor.editButtonFunc != null) + { + this.editor.editButtonFunc(); + } + else if (this.editor.editButtonLink == '_blank') + { + this.editor.editAsNew(this.getEditBlankXml()); + } + else + { + graph.openLink(this.editor.editButtonLink, 'editWindow'); + } + + mxEvent.consume(evt); + }), Editor.editLargeImage, mxResources.get('edit')); + } + + if (this.lightboxToolbarActions != null) + { + for (var i = 0; i < this.lightboxToolbarActions.length; i++) + { + var lbAction = this.lightboxToolbarActions[i]; + addButton(lbAction.fn, lbAction.icon, lbAction.tooltip); + } + } + + if (toolbarConfig.refreshBtn != null) + { + addButton(mxUtils.bind(this, function(evt) + { + if (toolbarConfig.refreshBtn.url) + { + window.location.href = toolbarConfig.refreshBtn.url; + } + else + { + window.location.reload(); + } + + mxEvent.consume(evt); + }), Editor.refreshLargeImage, mxResources.get('refresh', null, 'Refresh')); + } + + if (toolbarConfig.fullscreenBtn != null && window.self !== window.top) + { + addButton(mxUtils.bind(this, function(evt) + { + if (toolbarConfig.fullscreenBtn.url) + { + graph.openLink(toolbarConfig.fullscreenBtn.url); + } + else + { + graph.openLink(window.location.href); + } + + mxEvent.consume(evt); + }), Editor.fullscreenLargeImage, mxResources.get('openInNewWindow', null, 'Open in New Window')); + } + + if ((toolbarConfig.closeBtn && window.self === window.top) || + (graph.lightbox && (urlParams['close'] == '1' || this.container != document.body))) + + { + addButton(mxUtils.bind(this, function(evt) + { + if (urlParams['close'] == '1' || toolbarConfig.closeBtn) + { + window.close(); + } + else + { + this.destroy(); + mxEvent.consume(evt); + } + }), Editor.closeLargeImage, mxResources.get('close') + ' (Escape)'); + } + + // Initial state invisible + this.chromelessToolbar.style.display = 'none'; + + if (!graph.isViewer()) + { + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transform', 'translate(-50%,0)'); + } + + graph.container.appendChild(this.chromelessToolbar); + + mxEvent.addListener(graph.container, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isTouchEvent(evt)) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(30); + } + + fadeOut(); + } + })); + + mxEvent.addListener(this.chromelessToolbar, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', function(evt) + { + mxEvent.consume(evt); + }); + + mxEvent.addListener(this.chromelessToolbar, 'mouseenter', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(100); + } + else + { + fadeOut(); + } + })); + + mxEvent.addListener(this.chromelessToolbar, 'mousemove', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(100); + } + else + { + fadeOut(); + } + + mxEvent.consume(evt); + })); + + mxEvent.addListener(this.chromelessToolbar, 'mouseleave', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isTouchEvent(evt)) + { + fadeIn(30); + } + })); + + // Shows/hides toolbar for touch devices + var tol = graph.getTolerance(); + + graph.addMouseListener( + { + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0, + mouseDown: function(sender, me) + { + this.startX = me.getGraphX(); + this.startY = me.getGraphY(); + this.scrollLeft = graph.container.scrollLeft; + this.scrollTop = graph.container.scrollTop; + }, + mouseMove: function(sender, me) {}, + mouseUp: function(sender, me) + { + if (mxEvent.isTouchEvent(me.getEvent())) + { + if ((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (Math.abs(this.startX - me.getGraphX()) < tol && + Math.abs(this.startY - me.getGraphY()) < tol)) + { + if (parseFloat(ui.chromelessToolbar.style.opacity || 0) > 0) + { + fadeOut(); + } + else + { + fadeIn(30); + } + } + } + } + }); + } // end if toolbar + + // Installs handling of highlight and handling links to relative links and anchors + if (!this.editor.editable) + { + this.addChromelessClickHandler(); + } + } + else if (this.editor.extendCanvas) + { + /** + * Guesses autoTranslate to avoid another repaint (see below). + * Works if only the scale of the graph changes or if pages + * are visible and the visible pages do not change. + */ + var graphViewValidate = graph.view.validate; + graph.view.validate = function() + { + if (this.graph.container != null && mxUtils.hasScrollbars(this.graph.container)) + { + var pad = this.graph.getPagePadding(); + var size = this.graph.getPageSize(); + + // Updating scrollbars here causes flickering in quirks and is not needed + // if zoom method is always used to set the current scale on the graph. + var tx = this.translate.x; + var ty = this.translate.y; + this.translate.x = pad.x - (this.x0 || 0) * size.width; + this.translate.y = pad.y - (this.y0 || 0) * size.height; + } + + graphViewValidate.apply(this, arguments); + }; + + if (!graph.isViewer()) + { + var graphSizeDidChange = graph.sizeDidChange; + + graph.sizeDidChange = function() + { + if (this.container != null && mxUtils.hasScrollbars(this.container)) + { + var pages = this.getPageLayout(); + var pad = this.getPagePadding(); + var size = this.getPageSize(); + + // Updates the minimum graph size + var minw = Math.ceil(2 * pad.x + pages.width * size.width); + var minh = Math.ceil(2 * pad.y + pages.height * size.height); + + var min = graph.minimumGraphSize; + + // LATER: Fix flicker of scrollbar size in IE quirks mode + // after delayed call in window.resize event handler + if (min == null || min.width != minw || min.height != minh) + { + graph.minimumGraphSize = new mxRectangle(0, 0, minw, minh); + } + + // Updates auto-translate to include padding and graph size + var dx = pad.x - pages.x * size.width; + var dy = pad.y - pages.y * size.height; + + if (!this.autoTranslate && (this.view.translate.x != dx || this.view.translate.y != dy)) + { + this.autoTranslate = true; + this.view.x0 = pages.x; + this.view.y0 = pages.y; + + // NOTE: THIS INVOKES THIS METHOD AGAIN. UNFORTUNATELY THERE IS NO WAY AROUND THIS SINCE THE + // BOUNDS ARE KNOWN AFTER THE VALIDATION AND SETTING THE TRANSLATE TRIGGERS A REVALIDATION. + // SHOULD MOVE TRANSLATE/SCALE TO VIEW. + var tx = graph.view.translate.x; + var ty = graph.view.translate.y; + graph.view.setTranslate(dx, dy); + + // LATER: Fix rounding errors for small zoom + graph.container.scrollLeft += Math.round((dx - tx) * graph.view.scale); + graph.container.scrollTop += Math.round((dy - ty) * graph.view.scale); + + this.autoTranslate = false; + + return; + } + + graphSizeDidChange.apply(this, arguments); + } + else + { + // Fires event but does not invoke superclass + this.fireEvent(new mxEventObject(mxEvent.SIZE, 'bounds', this.getGraphBounds())); + } + }; + } + } + + // Accumulates the zoom factor while the rendering is taking place + // so that not the complete sequence of zoom steps must be painted + var bgGroup = graph.view.getBackgroundPane(); + var mainGroup = graph.view.getDrawPane(); + graph.cumulativeZoomFactor = 1; + var updateZoomTimeout = null; + var cursorPosition = null; + var scrollPosition = null; + var filter = null; + + var scheduleZoom = function(delay) + { + if (updateZoomTimeout != null) + { + window.clearTimeout(updateZoomTimeout); + } + + window.setTimeout(function() + { + if (!graph.isMouseDown) + { + updateZoomTimeout = window.setTimeout(mxUtils.bind(this, function() + { + if (graph.isFastZoomEnabled()) + { + // Transforms background page + if (graph.view.backgroundPageShape != null && graph.view.backgroundPageShape.node != null) + { + mxUtils.setPrefixedStyle(graph.view.backgroundPageShape.node.style, 'transform-origin', null); + mxUtils.setPrefixedStyle(graph.view.backgroundPageShape.node.style, 'transform', null); + } + + // Transforms graph and background image + mainGroup.style.transformOrigin = ''; + bgGroup.style.transformOrigin = ''; + + // Workaround for no reset of transform in Safari + if (mxClient.IS_SF) + { + mainGroup.style.transform = 'scale(1)'; + bgGroup.style.transform = 'scale(1)'; + + window.setTimeout(function() + { + mainGroup.style.transform = ''; + bgGroup.style.transform = ''; + }, 0) + } + else + { + mainGroup.style.transform = ''; + bgGroup.style.transform = ''; + } + + // Shows interactive elements + graph.view.getDecoratorPane().style.opacity = ''; + graph.view.getOverlayPane().style.opacity = ''; + } + + var sp = new mxPoint(graph.container.scrollLeft, graph.container.scrollTop); + var offset = mxUtils.getOffset(graph.container); + var prev = graph.view.scale; + var dx = 0; + var dy = 0; + + if (cursorPosition != null) + { + dx = graph.container.offsetWidth / 2 - cursorPosition.x + offset.x; + dy = graph.container.offsetHeight / 2 - cursorPosition.y + offset.y; + } + + graph.zoom(graph.cumulativeZoomFactor); + var s = graph.view.scale; + + if (s != prev) + { + if (scrollPosition != null) + { + dx += sp.x - scrollPosition.x; + dy += sp.y - scrollPosition.y; + } + + if (resize != null) + { + ui.chromelessResize(false, null, dx * (graph.cumulativeZoomFactor - 1), + dy * (graph.cumulativeZoomFactor - 1)); + } + + if (mxUtils.hasScrollbars(graph.container) && (dx != 0 || dy != 0)) + { + graph.container.scrollLeft -= dx * (graph.cumulativeZoomFactor - 1); + graph.container.scrollTop -= dy * (graph.cumulativeZoomFactor - 1); + } + } + + if (filter != null) + { + mainGroup.setAttribute('filter', filter); + } + + graph.cumulativeZoomFactor = 1; + updateZoomTimeout = null; + scrollPosition = null; + cursorPosition = null; + filter = null; + }), (delay != null) ? delay : ((graph.isFastZoomEnabled()) ? ui.wheelZoomDelay : ui.lazyZoomDelay)); + } + }, 0); + }; + + graph.lazyZoom = function(zoomIn, ignoreCursorPosition, delay) + { + // TODO: Fix ignored cursor position if scrollbars are disabled + ignoreCursorPosition = ignoreCursorPosition || !graph.scrollbars; + + if (ignoreCursorPosition) + { + cursorPosition = new mxPoint( + graph.container.offsetLeft + graph.container.clientWidth / 2, + graph.container.offsetTop + graph.container.clientHeight / 2); + } + + // Switches to 5% zoom steps below 15% + if (zoomIn) + { + if (this.view.scale * this.cumulativeZoomFactor <= 0.15) + { + this.cumulativeZoomFactor *= (this.view.scale + 0.05) / this.view.scale; + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.cumulativeZoomFactor *= this.zoomFactor; + this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; + } + } + else + { + if (this.view.scale * this.cumulativeZoomFactor <= 0.15) + { + this.cumulativeZoomFactor *= (this.view.scale - 0.05) / this.view.scale; + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.cumulativeZoomFactor /= this.zoomFactor; + this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; + } + } + + this.cumulativeZoomFactor = Math.max(0.05, Math.min(this.view.scale * this.cumulativeZoomFactor, 160)) / this.view.scale; + + if (graph.isFastZoomEnabled()) + { + if (filter == null && mainGroup.getAttribute('filter') != '') + { + filter = mainGroup.getAttribute('filter'); + mainGroup.removeAttribute('filter'); + } + + scrollPosition = new mxPoint(graph.container.scrollLeft, graph.container.scrollTop); + + var cx = (ignoreCursorPosition) ? graph.container.scrollLeft + graph.container.clientWidth / 2 : + cursorPosition.x + graph.container.scrollLeft - graph.container.offsetLeft; + var cy = (ignoreCursorPosition) ? graph.container.scrollTop + graph.container.clientHeight / 2 : + cursorPosition.y + graph.container.scrollTop - graph.container.offsetTop; + mainGroup.style.transformOrigin = cx + 'px ' + cy + 'px'; + mainGroup.style.transform = 'scale(' + this.cumulativeZoomFactor + ')'; + bgGroup.style.transformOrigin = cx + 'px ' + cy + 'px'; + bgGroup.style.transform = 'scale(' + this.cumulativeZoomFactor + ')'; + + if (graph.view.backgroundPageShape != null && graph.view.backgroundPageShape.node != null) + { + var page = graph.view.backgroundPageShape.node; + + mxUtils.setPrefixedStyle(page.style, 'transform-origin', + ((ignoreCursorPosition) ? ((graph.container.clientWidth / 2 + graph.container.scrollLeft - + page.offsetLeft) + 'px') : ((cursorPosition.x + graph.container.scrollLeft - + page.offsetLeft - graph.container.offsetLeft) + 'px')) + ' ' + + ((ignoreCursorPosition) ? ((graph.container.clientHeight / 2 + graph.container.scrollTop - + page.offsetTop) + 'px') : ((cursorPosition.y + graph.container.scrollTop - + page.offsetTop - graph.container.offsetTop) + 'px'))); + mxUtils.setPrefixedStyle(page.style, 'transform', + 'scale(' + this.cumulativeZoomFactor + ')'); + } + + graph.view.getDecoratorPane().style.opacity = '0'; + graph.view.getOverlayPane().style.opacity = '0'; + + if (ui.hoverIcons != null) + { + ui.hoverIcons.reset(); + } + } + + scheduleZoom(delay); + }; + + // Holds back repaint until after mouse gestures + mxEvent.addGestureListeners(graph.container, function(evt) + { + if (updateZoomTimeout != null) + { + window.clearTimeout(updateZoomTimeout); + } + }, null, function(evt) + { + if (graph.cumulativeZoomFactor != 1) + { + scheduleZoom(0); + } + }); + + // Holds back repaint until scroll ends + mxEvent.addListener(graph.container, 'scroll', function() + { + if (updateZoomTimeout && !graph.isMouseDown && graph.cumulativeZoomFactor != 1) + { + scheduleZoom(0); + } + }); + + mxEvent.addMouseWheelListener(mxUtils.bind(this, function(evt, up, force) + { + if (this.dialogs == null || this.dialogs.length == 0) + { + // Scrolls with scrollbars turned off + if (!graph.scrollbars && graph.isScrollWheelEvent(evt)) + { + var t = graph.view.getTranslate(); + var step = 40 / graph.view.scale; + + if (!mxEvent.isShiftDown(evt)) + { + graph.view.setTranslate(t.x, t.y + ((up) ? step : -step)); + } + else + { + graph.view.setTranslate(t.x + ((up) ? -step : step), t.y); + } + } + else if (force || graph.isZoomWheelEvent(evt)) + { + var source = mxEvent.getSource(evt); + + while (source != null) + { + if (source == graph.container) + { + graph.tooltipHandler.hideTooltip(); + cursorPosition = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + graph.lazyZoom(up); + mxEvent.consume(evt); + + return false; + } + + source = source.parentNode; + } + } + } + }), graph.container); + + // Uses fast zoom for pinch gestures on iOS + graph.panningHandler.zoomGraph = function(evt) + { + graph.cumulativeZoomFactor = evt.scale; + graph.lazyZoom(evt.scale > 0, true); + mxEvent.consume(evt); + }; +}; + +/** + * Creates a temporary graph instance for rendering off-screen content. + */ +EditorUi.prototype.addChromelessToolbarItems = function(addButton) +{ + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('print').funct(); + mxEvent.consume(evt); + }), Editor.printLargeImage, mxResources.get('print')); +}; + +/** + * Creates a temporary graph instance for rendering off-screen content. + */ +EditorUi.prototype.createTemporaryGraph = function(stylesheet) +{ + var graph = new Graph(document.createElement('div'), null, null, stylesheet); + graph.resetViewOnRootChange = false; + graph.setConnectable(false); + graph.gridEnabled = false; + graph.autoScroll = false; + graph.setTooltips(false); + graph.setEnabled(false); + + // Container must be in the DOM for correct HTML rendering + graph.container.style.visibility = 'hidden'; + graph.container.style.position = 'absolute'; + graph.container.style.overflow = 'hidden'; + graph.container.style.height = '1px'; + graph.container.style.width = '1px'; + + return graph; +}; + +/** + * + */ +EditorUi.prototype.addChromelessClickHandler = function() +{ + var hl = urlParams['highlight']; + + // Adds leading # for highlight color code + if (hl != null && hl.length > 0) + { + hl = '#' + hl; + } + + this.editor.graph.addClickHandler(hl); +}; + +/** + * + */ +EditorUi.prototype.toggleFormatPanel = function(forceHide) +{ + if (this.format != null) + { + this.formatWidth = (forceHide || this.formatWidth > 0) ? 0 : 240; + this.formatContainer.style.display = (forceHide || this.formatWidth > 0) ? '' : 'none'; + this.refresh(); + this.format.refresh(); + this.fireEvent(new mxEventObject('formatWidthChanged')); + } +}; + +/** + * Adds support for placeholders in labels. + */ +EditorUi.prototype.lightboxFit = function(maxHeight) +{ + if (this.isDiagramEmpty()) + { + this.editor.graph.view.setScale(1); + } + else + { + var p = urlParams['border']; + var border = 60; + + if (p != null) + { + border = parseInt(p); + } + + // LATER: Use initial graph bounds to avoid rounding errors + this.editor.graph.maxFitScale = this.lightboxMaxFitScale; + this.editor.graph.fit(border, null, null, null, null, null, maxHeight); + this.editor.graph.maxFitScale = null; + } +}; + +/** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ +EditorUi.prototype.isDiagramEmpty = function() +{ + var model = this.editor.graph.getModel(); + + return model.getChildCount(model.root) == 1 && model.getChildCount(model.getChildAt(model.root, 0)) == 0; +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.isSelectionAllowed = function(evt) +{ + return mxEvent.getSource(evt).nodeName == 'SELECT' || (mxEvent.getSource(evt).nodeName == 'INPUT' && + mxUtils.isAncestorNode(this.formatContainer, mxEvent.getSource(evt))); +}; + +/** + * Installs dialog if browser window is closed without saving + * This must be disabled during save and image export. + */ +EditorUi.prototype.addBeforeUnloadListener = function() +{ + // Installs dialog if browser window is closed without saving + // This must be disabled during save and image export + window.onbeforeunload = mxUtils.bind(this, function() + { + if (!this.editor.isChromelessView()) + { + return this.onBeforeUnload(); + } + }); +}; + +/** + * Sets the onbeforeunload for the application + */ +EditorUi.prototype.onBeforeUnload = function() +{ + if (this.editor.modified) + { + return mxResources.get('allChangesLost'); + } +}; + +/** + * Opens the current diagram via the window.opener if one exists. + */ +EditorUi.prototype.open = function() +{ + // Cross-domain window access is not allowed in FF, so if we + // were opened from another domain then this will fail. + try + { + if (window.opener != null && window.opener.openFile != null) + { + window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) + { + try + { + var doc = mxUtils.parseXml(xml); + this.editor.setGraphXml(doc.documentElement); + this.editor.setModified(false); + this.editor.undoManager.clear(); + + if (filename != null) + { + this.editor.setFilename(filename); + this.updateDocumentTitle(); + } + + return; + } + catch (e) + { + mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); + } + })); + } + } + catch(e) + { + // ignore + } + + // Fires as the last step if no file was loaded + this.editor.graph.view.validate(); + + // Required only in special cases where an initial file is opened + // and the minimumGraphSize changes and CSS must be updated. + this.editor.graph.sizeDidChange(); + this.editor.fireEvent(new mxEventObject('resetGraphView')); +}; + +/** + * Sets the current menu and element. + */ +EditorUi.prototype.setCurrentMenu = function(menu, elt) +{ + this.currentMenuElt = elt; + this.currentMenu = menu; +}; + +/** + * Resets the current menu and element. + */ +EditorUi.prototype.resetCurrentMenu = function() +{ + this.currentMenuElt = null; + this.currentMenu = null; +}; + +/** + * Hides and destroys the current menu. + */ +EditorUi.prototype.hideCurrentMenu = function() +{ + if (this.currentMenu != null) + { + this.currentMenu.hideMenu(); + this.resetCurrentMenu(); + } +}; + +/** + * Updates the document title. + */ +EditorUi.prototype.updateDocumentTitle = function() +{ + var title = this.editor.getOrCreateFilename(); + + if (this.editor.appName != null) + { + title += ' - ' + this.editor.appName; + } + + document.title = title; +}; + +/** + * Updates the document title. + */ +EditorUi.prototype.createHoverIcons = function() +{ + return new HoverIcons(this.editor.graph); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.redo = function() +{ + try + { + var graph = this.editor.graph; + + if (graph.isEditing()) + { + document.execCommand('redo', false, null); + } + else + { + this.editor.undoManager.redo(); + } + } + catch (e) + { + // ignore all errors + } +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.undo = function() +{ + try + { + var graph = this.editor.graph; + + if (graph.isEditing()) + { + // Stops editing and executes undo on graph if native undo + // does not affect current editing value + var value = graph.cellEditor.textarea.innerHTML; + document.execCommand('undo', false, null); + + if (value == graph.cellEditor.textarea.innerHTML) + { + graph.stopEditing(true); + this.editor.undoManager.undo(); + } + } + else + { + this.editor.undoManager.undo(); + } + } + catch (e) + { + // ignore all errors + } +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.canRedo = function() +{ + return this.editor.graph.isEditing() || this.editor.undoManager.canRedo(); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.canUndo = function() +{ + return this.editor.graph.isEditing() || this.editor.undoManager.canUndo(); +}; + +/** + * + */ +EditorUi.prototype.getEditBlankXml = function() +{ + return mxUtils.getXml(this.editor.getGraphXml()); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.getUrl = function(pathname) +{ + var href = (pathname != null) ? pathname : window.location.pathname; + var parms = (href.indexOf('?') > 0) ? 1 : 0; + + // Removes template URL parameter for new blank diagram + for (var key in urlParams) + { + if (parms == 0) + { + href += '?'; + } + else + { + href += '&'; + } + + href += key + '=' + urlParams[key]; + parms++; + } + + return href; +}; + +/** + * Specifies if the graph has scrollbars. + */ +EditorUi.prototype.setScrollbars = function(value) +{ + var graph = this.editor.graph; + var prev = graph.container.style.overflow; + graph.scrollbars = value; + this.editor.updateGraphComponents(); + + if (prev != graph.container.style.overflow) + { + graph.container.scrollTop = 0; + graph.container.scrollLeft = 0; + graph.view.scaleAndTranslate(1, 0, 0); + this.resetScrollbars(); + } + + this.fireEvent(new mxEventObject('scrollbarsChanged')); +}; + +/** + * Returns true if the graph has scrollbars. + */ +EditorUi.prototype.hasScrollbars = function() +{ + return this.editor.graph.scrollbars; +}; + +/** + * Resets the state of the scrollbars. + */ +EditorUi.prototype.resetScrollbars = function() +{ + var graph = this.editor.graph; + + if (!this.editor.extendCanvas) + { + graph.container.scrollTop = 0; + graph.container.scrollLeft = 0; + + if (!mxUtils.hasScrollbars(graph.container)) + { + graph.view.setTranslate(0, 0); + } + } + else if (!this.editor.isChromelessView()) + { + if (mxUtils.hasScrollbars(graph.container)) + { + if (graph.pageVisible) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = Math.floor(pad.y - this.editor.initialTopSpacing) - 1; + graph.container.scrollLeft = Math.floor(Math.min(pad.x, + (graph.container.scrollWidth - graph.container.clientWidth) / 2)) - 1; + + // Scrolls graph to visible area + var bounds = graph.getGraphBounds(); + + if (bounds.width > 0 && bounds.height > 0) + { + if (bounds.x > graph.container.scrollLeft + graph.container.clientWidth * 0.9) + { + graph.container.scrollLeft = Math.min(bounds.x + bounds.width - graph.container.clientWidth, bounds.x - 10); + } + + if (bounds.y > graph.container.scrollTop + graph.container.clientHeight * 0.9) + { + graph.container.scrollTop = Math.min(bounds.y + bounds.height - graph.container.clientHeight, bounds.y - 10); + } + } + } + else + { + var bounds = graph.getGraphBounds(); + var width = Math.max(bounds.width, graph.scrollTileSize.width * graph.view.scale); + var height = Math.max(bounds.height, graph.scrollTileSize.height * graph.view.scale); + graph.container.scrollTop = Math.floor(Math.max(0, bounds.y - Math.max(20, (graph.container.clientHeight - height) / 4))); + graph.container.scrollLeft = Math.floor(Math.max(0, bounds.x - Math.max(0, (graph.container.clientWidth - width) / 2))); + } + } + else + { + var b = mxRectangle.fromRectangle((graph.pageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds()) + var tr = graph.view.translate; + var s = graph.view.scale; + b.x = b.x / s - tr.x; + b.y = b.y / s - tr.y; + b.width /= s; + b.height /= s; + + var dy = (graph.pageVisible) ? 0 : Math.max(0, (graph.container.clientHeight - b.height) / 4); + + graph.view.setTranslate(Math.floor(Math.max(0, + (graph.container.clientWidth - b.width) / 2) - b.x + 2), + Math.floor(dy - b.y + 1)); + } + } +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageVisible = function(value) +{ + var graph = this.editor.graph; + var hasScrollbars = mxUtils.hasScrollbars(graph.container); + var tx = 0; + var ty = 0; + + if (hasScrollbars) + { + tx = graph.view.translate.x * graph.view.scale - graph.container.scrollLeft; + ty = graph.view.translate.y * graph.view.scale - graph.container.scrollTop; + } + + graph.pageVisible = value; + graph.pageBreaksVisible = value; + graph.preferPageSize = value; + graph.view.validateBackground(); + + // Workaround for possible handle offset + if (hasScrollbars) + { + var cells = graph.getSelectionCells(); + graph.clearSelection(); + graph.setSelectionCells(cells); + } + + // Calls updatePageBreaks + graph.sizeDidChange(); + + if (hasScrollbars) + { + graph.container.scrollLeft = graph.view.translate.x * graph.view.scale - tx; + graph.container.scrollTop = graph.view.translate.y * graph.view.scale - ty; + } + + this.fireEvent(new mxEventObject('pageViewChanged')); +}; + +/** + * Change types + */ +function ChangePageSetup(ui, color, image, format, pageScale) +{ + this.ui = ui; + this.color = color; + this.previousColor = color; + this.image = image; + this.previousImage = image; + this.format = format; + this.previousFormat = format; + this.pageScale = pageScale; + this.previousPageScale = pageScale; + + // Needed since null are valid values for color and image + this.ignoreColor = false; + this.ignoreImage = false; +} + +/** + * Implementation of the undoable page rename. + */ +ChangePageSetup.prototype.execute = function() +{ + var graph = this.ui.editor.graph; + + if (!this.ignoreColor) + { + this.color = this.previousColor; + var tmp = graph.background; + this.ui.setBackgroundColor(this.previousColor); + this.previousColor = tmp; + } + + if (!this.ignoreImage) + { + this.image = this.previousImage; + var tmp = graph.backgroundImage; + this.ui.setBackgroundImage(this.previousImage); + this.previousImage = tmp; + } + + if (this.previousFormat != null) + { + this.format = this.previousFormat; + var tmp = graph.pageFormat; + + if (this.previousFormat.width != tmp.width || + this.previousFormat.height != tmp.height) + { + this.ui.setPageFormat(this.previousFormat); + this.previousFormat = tmp; + } + } + + if (this.foldingEnabled != null && this.foldingEnabled != this.ui.editor.graph.foldingEnabled) + { + this.ui.setFoldingEnabled(this.foldingEnabled); + this.foldingEnabled = !this.foldingEnabled; + } + + if (this.previousPageScale != null) + { + var currentPageScale = this.ui.editor.graph.pageScale; + + if (this.previousPageScale != currentPageScale) + { + this.ui.setPageScale(this.previousPageScale); + this.previousPageScale = currentPageScale; + } + } +}; + +// Registers codec for ChangePageSetup +(function() +{ + var codec = new mxObjectCodec(new ChangePageSetup(), ['ui', 'previousColor', 'previousImage', 'previousFormat', 'previousPageScale']); + + codec.afterDecode = function(dec, node, obj) + { + obj.previousColor = obj.color; + obj.previousImage = obj.image; + obj.previousFormat = obj.format; + obj.previousPageScale = obj.pageScale; + + if (obj.foldingEnabled != null) + { + obj.foldingEnabled = !obj.foldingEnabled; + } + + return obj; + }; + + mxCodecRegistry.register(codec); +})(); + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setBackgroundColor = function(value) +{ + this.editor.graph.background = value; + this.editor.graph.view.validateBackground(); + + this.fireEvent(new mxEventObject('backgroundColorChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setFoldingEnabled = function(value) +{ + this.editor.graph.foldingEnabled = value; + this.editor.graph.view.revalidate(); + + this.fireEvent(new mxEventObject('foldingEnabledChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageFormat = function(value) +{ + this.editor.graph.pageFormat = value; + + if (!this.editor.graph.pageVisible) + { + this.actions.get('pageView').funct(); + } + else + { + this.editor.graph.view.validateBackground(); + this.editor.graph.sizeDidChange(); + } + + this.fireEvent(new mxEventObject('pageFormatChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageScale = function(value) +{ + this.editor.graph.pageScale = value; + + if (!this.editor.graph.pageVisible) + { + this.actions.get('pageView').funct(); + } + else + { + this.editor.graph.view.validateBackground(); + this.editor.graph.sizeDidChange(); + } + + this.fireEvent(new mxEventObject('pageScaleChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setGridColor = function(value) +{ + this.editor.graph.view.gridColor = value; + this.editor.graph.view.validateBackground(); + this.fireEvent(new mxEventObject('gridColorChanged')); +}; + +/** + * Updates the states of the given undo/redo items. + */ +EditorUi.prototype.addUndoListener = function() +{ + var undo = this.actions.get('undo'); + var redo = this.actions.get('redo'); + + var undoMgr = this.editor.undoManager; + + var undoListener = mxUtils.bind(this, function() + { + undo.setEnabled(this.canUndo()); + redo.setEnabled(this.canRedo()); + }); + + undoMgr.addListener(mxEvent.ADD, undoListener); + undoMgr.addListener(mxEvent.UNDO, undoListener); + undoMgr.addListener(mxEvent.REDO, undoListener); + undoMgr.addListener(mxEvent.CLEAR, undoListener); + + // Overrides cell editor to update action states + var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; + + this.editor.graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + undoListener(); + }; + + var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; + + this.editor.graph.cellEditor.stopEditing = function(cell, trigger) + { + cellEditorStopEditing.apply(this, arguments); + undoListener(); + }; + + // Updates the button states once + undoListener(); +}; + +/** +* Updates the states of the given toolbar items based on the selection. +*/ +EditorUi.prototype.updateActionStates = function() +{ + var graph = this.editor.graph; + var selected = !graph.isSelectionEmpty(); + var vertexSelected = false; + var edgeSelected = false; + + var cells = graph.getSelectionCells(); + + if (cells != null) + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + edgeSelected = true; + } + + if (graph.getModel().isVertex(cell)) + { + vertexSelected = true; + } + + if (edgeSelected && vertexSelected) + { + break; + } + } + } + + // Updates action states + var actions = ['cut', 'copy', 'bold', 'italic', 'underline', 'delete', 'duplicate', + 'editStyle', 'editTooltip', 'editLink', 'backgroundColor', 'borderColor', + 'edit', 'toFront', 'toBack', 'lockUnlock', 'solid', 'dashed', 'pasteSize', + 'dotted', 'fillColor', 'gradientColor', 'shadow', 'fontColor', + 'formattedText', 'rounded', 'toggleRounded', 'sharp', 'strokeColor']; + + for (var i = 0; i < actions.length; i++) + { + this.actions.get(actions[i]).setEnabled(selected); + } + + this.actions.get('setAsDefaultStyle').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('clearWaypoints').setEnabled(!graph.isSelectionEmpty()); + this.actions.get('copySize').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('turn').setEnabled(!graph.isSelectionEmpty()); + this.actions.get('curved').setEnabled(edgeSelected); + this.actions.get('rotation').setEnabled(vertexSelected); + this.actions.get('wordWrap').setEnabled(vertexSelected); + this.actions.get('autosize').setEnabled(vertexSelected); + var oneVertexSelected = vertexSelected && graph.getSelectionCount() == 1; + this.actions.get('group').setEnabled(graph.getSelectionCount() > 1 || + (oneVertexSelected && !graph.isContainer(graph.getSelectionCell()))); + this.actions.get('ungroup').setEnabled(graph.getSelectionCount() == 1 && + (graph.getModel().getChildCount(graph.getSelectionCell()) > 0 || + (oneVertexSelected && graph.isContainer(graph.getSelectionCell())))); + this.actions.get('removeFromGroup').setEnabled(oneVertexSelected && + graph.getModel().isVertex(graph.getModel().getParent(graph.getSelectionCell()))); + + // Updates menu states + var state = graph.view.getState(graph.getSelectionCell()); + this.menus.get('navigation').setEnabled(selected || graph.view.currentRoot != null); + this.actions.get('collapsible').setEnabled(vertexSelected && + (graph.isContainer(graph.getSelectionCell()) || graph.model.getChildCount(graph.getSelectionCell()) > 0)); + this.actions.get('home').setEnabled(graph.view.currentRoot != null); + this.actions.get('exitGroup').setEnabled(graph.view.currentRoot != null); + this.actions.get('enterGroup').setEnabled(graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell())); + var foldable = graph.getSelectionCount() == 1 && graph.isCellFoldable(graph.getSelectionCell()); + this.actions.get('expand').setEnabled(foldable); + this.actions.get('collapse').setEnabled(foldable); + this.actions.get('editLink').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('openLink').setEnabled(graph.getSelectionCount() == 1 && + graph.getLinkForCell(graph.getSelectionCell()) != null); + this.actions.get('guides').setEnabled(graph.isEnabled()); + this.actions.get('grid').setEnabled(!this.editor.chromeless || this.editor.editable); + + var unlocked = graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()); + this.menus.get('layout').setEnabled(unlocked); + this.menus.get('insert').setEnabled(unlocked); + this.menus.get('direction').setEnabled(unlocked && vertexSelected); + this.menus.get('align').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); + this.menus.get('distribute').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); + this.actions.get('selectVertices').setEnabled(unlocked); + this.actions.get('selectEdges').setEnabled(unlocked); + this.actions.get('selectAll').setEnabled(unlocked); + this.actions.get('selectNone').setEnabled(unlocked); + + this.updatePasteActionStates(); +}; + +EditorUi.prototype.zeroOffset = new mxPoint(0, 0); + +EditorUi.prototype.getDiagramContainerOffset = function() +{ + return this.zeroOffset; +}; + +/** + * Refreshes the viewport. + */ +EditorUi.prototype.refresh = function(sizeDidChange) +{ + sizeDidChange = (sizeDidChange != null) ? sizeDidChange : true; + + var quirks = mxClient.IS_IE && (document.documentMode == null || document.documentMode == 5); + var w = this.container.clientWidth; + var h = this.container.clientHeight; + + if (this.container == document.body) + { + w = document.body.clientWidth || document.documentElement.clientWidth; + h = (quirks) ? document.body.clientHeight || document.documentElement.clientHeight : document.documentElement.clientHeight; + } + + // Workaround for bug on iOS see + // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue + // FIXME: Fix if footer visible + var off = 0; + + if (mxClient.IS_IOS && !window.navigator.standalone) + { + if (window.innerHeight != document.documentElement.clientHeight) + { + off = document.documentElement.clientHeight - window.innerHeight; + window.scrollTo(0, 0); + } + } + + var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize - 20)); + var tmp = 0; + + if (this.menubar != null) + { + this.menubarContainer.style.height = this.menubarHeight + 'px'; + tmp += this.menubarHeight; + } + + if (this.toolbar != null) + { + this.toolbarContainer.style.top = this.menubarHeight + 'px'; + this.toolbarContainer.style.height = this.toolbarHeight + 'px'; + tmp += this.toolbarHeight; + } + + if (tmp > 0 && !mxClient.IS_QUIRKS) + { + tmp += 1; + } + + var sidebarFooterHeight = 0; + + if (this.sidebarFooterContainer != null) + { + var bottom = this.footerHeight + off; + sidebarFooterHeight = Math.max(0, Math.min(h - tmp - bottom, this.sidebarFooterHeight)); + this.sidebarFooterContainer.style.width = effHsplitPosition + 'px'; + this.sidebarFooterContainer.style.height = sidebarFooterHeight + 'px'; + this.sidebarFooterContainer.style.bottom = bottom + 'px'; + } + + var fw = (this.format != null) ? this.formatWidth : 0; + this.sidebarContainer.style.top = tmp + 'px'; + this.sidebarContainer.style.width = effHsplitPosition + 'px'; + this.formatContainer.style.top = tmp + 'px'; + this.formatContainer.style.width = fw + 'px'; + this.formatContainer.style.display = (this.format != null) ? '' : 'none'; + + var diagContOffset = this.getDiagramContainerOffset(); + var contLeft = (this.hsplit.parentNode != null) ? (effHsplitPosition + this.splitSize) : 0; + this.diagramContainer.style.left = (contLeft + diagContOffset.x) + 'px'; + this.diagramContainer.style.top = (tmp + diagContOffset.y) + 'px'; + this.footerContainer.style.height = this.footerHeight + 'px'; + this.hsplit.style.top = this.sidebarContainer.style.top; + this.hsplit.style.bottom = (this.footerHeight + off) + 'px'; + this.hsplit.style.left = effHsplitPosition + 'px'; + this.footerContainer.style.display = (this.footerHeight == 0) ? 'none' : ''; + + if (this.tabContainer != null) + { + this.tabContainer.style.left = contLeft + 'px'; + } + + if (quirks) + { + this.menubarContainer.style.width = w + 'px'; + this.toolbarContainer.style.width = this.menubarContainer.style.width; + var sidebarHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); + this.sidebarContainer.style.height = (sidebarHeight - sidebarFooterHeight) + 'px'; + this.formatContainer.style.height = sidebarHeight + 'px'; + this.diagramContainer.style.width = (this.hsplit.parentNode != null) ? Math.max(0, w - effHsplitPosition - this.splitSize - fw) + 'px' : w + 'px'; + this.footerContainer.style.width = this.menubarContainer.style.width; + var diagramHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); + + if (this.tabContainer != null) + { + this.tabContainer.style.width = this.diagramContainer.style.width; + this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; + diagramHeight -= this.tabContainer.clientHeight; + } + + this.diagramContainer.style.height = diagramHeight + 'px'; + this.hsplit.style.height = diagramHeight + 'px'; + } + else + { + if (this.footerHeight > 0) + { + this.footerContainer.style.bottom = off + 'px'; + } + + this.diagramContainer.style.right = fw + 'px'; + var th = 0; + + if (this.tabContainer != null) + { + this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; + this.tabContainer.style.right = this.diagramContainer.style.right; + th = this.tabContainer.clientHeight; + } + + this.sidebarContainer.style.bottom = (this.footerHeight + sidebarFooterHeight + off) + 'px'; + this.formatContainer.style.bottom = (this.footerHeight + off) + 'px'; + this.diagramContainer.style.bottom = (this.footerHeight + off + th) + 'px'; + } + + if (sizeDidChange) + { + this.editor.graph.sizeDidChange(); + } +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createTabContainer = function() +{ + return null; +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createDivs = function() +{ + this.menubarContainer = this.createDiv('geMenubarContainer'); + this.toolbarContainer = this.createDiv('geToolbarContainer'); + this.sidebarContainer = this.createDiv('geSidebarContainer'); + this.formatContainer = this.createDiv('geSidebarContainer geFormatContainer'); + this.diagramContainer = this.createDiv('geDiagramContainer'); + this.footerContainer = this.createDiv('geFooterContainer'); + this.hsplit = this.createDiv('geHsplit'); + this.hsplit.setAttribute('title', mxResources.get('collapseExpand')); + + // Sets static style for containers + this.menubarContainer.style.top = '0px'; + this.menubarContainer.style.left = '0px'; + this.menubarContainer.style.right = '0px'; + this.toolbarContainer.style.left = '0px'; + this.toolbarContainer.style.right = '0px'; + this.sidebarContainer.style.left = '0px'; + this.formatContainer.style.right = '0px'; + this.formatContainer.style.zIndex = '1'; + this.diagramContainer.style.right = ((this.format != null) ? this.formatWidth : 0) + 'px'; + this.footerContainer.style.left = '0px'; + this.footerContainer.style.right = '0px'; + this.footerContainer.style.bottom = '0px'; + this.footerContainer.style.zIndex = mxPopupMenu.prototype.zIndex - 2; + this.hsplit.style.width = this.splitSize + 'px'; + this.sidebarFooterContainer = this.createSidebarFooterContainer(); + + if (this.sidebarFooterContainer) + { + this.sidebarFooterContainer.style.left = '0px'; + } + + if (!this.editor.chromeless) + { + this.tabContainer = this.createTabContainer(); + } + else + { + this.diagramContainer.style.border = 'none'; + } +}; + +/** + * Hook for sidebar footer container. This implementation returns null. + */ +EditorUi.prototype.createSidebarFooterContainer = function() +{ + return null; +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createUi = function() +{ + // Creates menubar + this.menubar = (this.editor.chromeless) ? null : this.menus.createMenubar(this.createDiv('geMenubar')); + + if (this.menubar != null) + { + this.menubarContainer.appendChild(this.menubar.container); + } + + // Adds status bar in menubar + if (this.menubar != null) + { + this.statusContainer = this.createStatusContainer(); + + // Connects the status bar to the editor status + this.editor.addListener('statusChanged', mxUtils.bind(this, function() + { + this.setStatusText(this.editor.getStatus()); + })); + + this.setStatusText(this.editor.getStatus()); + this.menubar.container.appendChild(this.statusContainer); + + // Inserts into DOM + this.container.appendChild(this.menubarContainer); + } + + // Creates the sidebar + this.sidebar = (this.editor.chromeless) ? null : this.createSidebar(this.sidebarContainer); + + if (this.sidebar != null) + { + this.container.appendChild(this.sidebarContainer); + } + + // Creates the format sidebar + this.format = (this.editor.chromeless || !this.formatEnabled) ? null : this.createFormat(this.formatContainer); + + if (this.format != null) + { + this.container.appendChild(this.formatContainer); + } + + // Creates the footer + var footer = (this.editor.chromeless) ? null : this.createFooter(); + + if (footer != null) + { + this.footerContainer.appendChild(footer); + this.container.appendChild(this.footerContainer); + } + + if (this.sidebar != null && this.sidebarFooterContainer) + { + this.container.appendChild(this.sidebarFooterContainer); + } + + this.container.appendChild(this.diagramContainer); + + if (this.container != null && this.tabContainer != null) + { + this.container.appendChild(this.tabContainer); + } + + // Creates toolbar + this.toolbar = (this.editor.chromeless) ? null : this.createToolbar(this.createDiv('geToolbar')); + + if (this.toolbar != null) + { + this.toolbarContainer.appendChild(this.toolbar.container); + this.container.appendChild(this.toolbarContainer); + } + + // HSplit + if (this.sidebar != null) + { + this.container.appendChild(this.hsplit); + + this.addSplitHandler(this.hsplit, true, 0, mxUtils.bind(this, function(value) + { + this.hsplitPosition = value; + this.refresh(); + })); + } +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.createStatusContainer = function() +{ + var container = document.createElement('a'); + container.className = 'geItem geStatus'; + + if (screen.width < 420) + { + container.style.maxWidth = Math.max(20, screen.width - 320) + 'px'; + container.style.overflow = 'hidden'; + } + + return container; +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.setStatusText = function(value) +{ + this.statusContainer.innerHTML = value; +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.createToolbar = function(container) +{ + return new Toolbar(this, container); +}; + +/** + * Creates a new sidebar for the given container. + */ +EditorUi.prototype.createSidebar = function(container) +{ + return new Sidebar(this, container); +}; + +/** + * Creates a new sidebar for the given container. + */ +EditorUi.prototype.createFormat = function(container) +{ + return new Format(this, container); +}; + +/** + * Creates and returns a new footer. + */ +EditorUi.prototype.createFooter = function() +{ + return this.createDiv('geFooter'); +}; + +/** + * Creates the actual toolbar for the toolbar container. + */ +EditorUi.prototype.createDiv = function(classname) +{ + var elt = document.createElement('div'); + elt.className = classname; + + return elt; +}; + +/** + * Updates the states of the given undo/redo items. + */ +EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange) +{ + var start = null; + var initial = null; + var ignoreClick = true; + var last = null; + + // Disables built-in pan and zoom in IE10 and later + if (mxClient.IS_POINTER) + { + elt.style.touchAction = 'none'; + } + + var getValue = mxUtils.bind(this, function() + { + var result = parseInt(((horizontal) ? elt.style.left : elt.style.bottom)); + + // Takes into account hidden footer + if (!horizontal) + { + result = result + dx - this.footerHeight; + } + + return result; + }); + + function moveHandler(evt) + { + if (start != null) + { + var pt = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + onChange(Math.max(0, initial + ((horizontal) ? (pt.x - start.x) : (start.y - pt.y)) - dx)); + mxEvent.consume(evt); + + if (initial != getValue()) + { + ignoreClick = true; + last = null; + } + } + }; + + function dropHandler(evt) + { + moveHandler(evt); + initial = null; + start = null; + }; + + mxEvent.addGestureListeners(elt, function(evt) + { + start = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + initial = getValue(); + ignoreClick = false; + mxEvent.consume(evt); + }); + + mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) + { + if (!ignoreClick && this.hsplitClickEnabled) + { + var next = (last != null) ? last - dx : 0; + last = getValue(); + onChange(next); + mxEvent.consume(evt); + } + })); + + mxEvent.addGestureListeners(document, null, moveHandler, dropHandler); + + this.destroyFunctions.push(function() + { + mxEvent.removeGestureListeners(document, null, moveHandler, dropHandler); + }); +}; + +/** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ +EditorUi.prototype.handleError = function(resp, title, fn, invokeFnOnClose, notFoundMessage) +{ + var e = (resp != null && resp.error != null) ? resp.error : resp; + + if (e != null || title != null) + { + var msg = mxUtils.htmlEntities(mxResources.get('unknownError')); + var btn = mxResources.get('ok'); + title = (title != null) ? title : mxResources.get('error'); + + if (e != null && e.message != null) + { + msg = mxUtils.htmlEntities(e.message); + } + + this.showError(title, msg, btn, fn, null, null, null, null, null, + null, null, null, (invokeFnOnClose) ? fn : null); + } + else if (fn != null) + { + fn(); + } +}; + +/** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ +EditorUi.prototype.showError = function(title, msg, btn, fn, retry, btn2, fn2, btn3, fn3, w, h, hide, onClose) +{ + var dlg = new ErrorDialog(this, title, msg, btn || mxResources.get('ok'), + fn, retry, btn2, fn2, hide, btn3, fn3); + var lines = Math.ceil((msg != null) ? msg.length / 50 : 1); + this.showDialog(dlg.container, w || 340, h || (100 + lines * 20), true, false, onClose); + dlg.init(); +}; + +/** + * Displays a print dialog. + */ +EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, noScroll, transparent, onResize, ignoreBgClick) +{ + this.editor.graph.tooltipHandler.hideTooltip(); + + if (this.dialogs == null) + { + this.dialogs = []; + } + + this.dialog = new Dialog(this, elt, w, h, modal, closable, onClose, noScroll, transparent, onResize, ignoreBgClick); + this.dialogs.push(this.dialog); +}; + +/** + * Displays a print dialog. + */ +EditorUi.prototype.hideDialog = function(cancel, isEsc) +{ + if (this.dialogs != null && this.dialogs.length > 0) + { + var dlg = this.dialogs.pop(); + + if (dlg.close(cancel, isEsc) == false) + { + //add the dialog back if dialog closing is cancelled + this.dialogs.push(dlg); + return; + } + + this.dialog = (this.dialogs.length > 0) ? this.dialogs[this.dialogs.length - 1] : null; + this.editor.fireEvent(new mxEventObject('hideDialog')); + + if (this.dialog == null && this.editor.graph.container.style.visibility != 'hidden') + { + window.setTimeout(mxUtils.bind(this, function() + { + if (this.editor.graph.isEditing() && this.editor.graph.cellEditor.textarea != null) + { + this.editor.graph.cellEditor.textarea.focus(); + } + else + { + mxUtils.clearSelection(); + this.editor.graph.container.focus(); + } + }), 0); + } + } +}; + +/** + * Display a color dialog. + */ +EditorUi.prototype.pickColor = function(color, apply) +{ + var graph = this.editor.graph; + var selState = graph.cellEditor.saveSelection(); + var h = 226 + ((Math.ceil(ColorDialog.prototype.presetColors.length / 12) + + Math.ceil(ColorDialog.prototype.defaultColors.length / 12)) * 17); + + var dlg = new ColorDialog(this, color || 'none', function(color) + { + graph.cellEditor.restoreSelection(selState); + apply(color); + }, function() + { + graph.cellEditor.restoreSelection(selState); + }); + this.showDialog(dlg.container, 230, h, true, false); + dlg.init(); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +EditorUi.prototype.openFile = function() +{ + // Closes dialog after open + window.openFile = new OpenFile(mxUtils.bind(this, function(cancel) + { + this.hideDialog(cancel); + })); + + // Removes openFile if dialog is closed + this.showDialog(new OpenDialog(this).container, (Editor.useLocalStorage) ? 640 : 320, + (Editor.useLocalStorage) ? 480 : 220, true, true, function() + { + window.openFile = null; + }); +}; + +/** + * Extracs the graph model from the given HTML data from a data transfer event. + */ +EditorUi.prototype.extractGraphModelFromHtml = function(data) +{ + var result = null; + + try + { + var idx = data.indexOf('<mxGraphModel '); + + if (idx >= 0) + { + var idx2 = data.lastIndexOf('</mxGraphModel>'); + + if (idx2 > idx) + { + result = data.substring(idx, idx2 + 21).replace(/>/g, '>'). + replace(/</g, '<').replace(/\\"/g, '"').replace(/\n/g, ''); + } + } + } + catch (e) + { + // ignore + } + + return result; +}; + +/** + * Opens the given files in the editor. + */ +EditorUi.prototype.extractGraphModelFromEvent = function(evt) +{ + var result = null; + var data = null; + + if (evt != null) + { + var provider = (evt.dataTransfer != null) ? evt.dataTransfer : evt.clipboardData; + + if (provider != null) + { + if (document.documentMode == 10 || document.documentMode == 11) + { + data = provider.getData('Text'); + } + else + { + data = (mxUtils.indexOf(provider.types, 'text/html') >= 0) ? provider.getData('text/html') : null; + + if (mxUtils.indexOf(provider.types, 'text/plain' && (data == null || data.length == 0))) + { + data = provider.getData('text/plain'); + } + } + + if (data != null) + { + data = Graph.zapGremlins(mxUtils.trim(data)); + + // Tries parsing as HTML document with embedded XML + var xml = this.extractGraphModelFromHtml(data); + + if (xml != null) + { + data = xml; + } + } + } + } + + if (data != null && this.isCompatibleString(data)) + { + result = data; + } + + return result; +}; + +/** + * Hook for subclassers to return true if event data is a supported format. + * This implementation always returns false. + */ +EditorUi.prototype.isCompatibleString = function(data) +{ + return false; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +EditorUi.prototype.saveFile = function(forceDialog) +{ + if (!forceDialog && this.editor.filename != null) + { + this.save(this.editor.getOrCreateFilename()); + } + else + { + var dlg = new FilenameDialog(this, this.editor.getOrCreateFilename(), mxResources.get('save'), mxUtils.bind(this, function(name) + { + this.save(name); + }), null, mxUtils.bind(this, function(name) + { + if (name != null && name.length > 0) + { + return true; + } + + mxUtils.confirm(mxResources.get('invalidName')); + + return false; + })); + this.showDialog(dlg.container, 300, 100, true, true); + dlg.init(); + } +}; + +/** + * Saves the current graph under the given filename. + */ +EditorUi.prototype.save = function(name) +{ + if (name != null) + { + if (this.editor.graph.isEditing()) + { + this.editor.graph.stopEditing(); + } + + var xml = mxUtils.getXml(this.editor.getGraphXml()); + + try + { + if (Editor.useLocalStorage) + { + if (localStorage.getItem(name) != null && + !mxUtils.confirm(mxResources.get('replaceIt', [name]))) + { + return; + } + + localStorage.setItem(name, xml); + this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('saved')) + ' ' + new Date()); + } + else + { + if (xml.length < MAX_REQUEST_SIZE) + { + new mxXmlRequest(SAVE_URL, 'filename=' + encodeURIComponent(name) + + '&xml=' + encodeURIComponent(xml)).simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + mxUtils.popup(xml); + + return; + } + } + + this.editor.setModified(false); + this.editor.setFilename(name); + this.updateDocumentTitle(); + } + catch (e) + { + this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('errorSavingFile'))); + } + } +}; + +/** + * Executes the given layout. + */ +EditorUi.prototype.executeLayout = function(exec, animate, post) +{ + var graph = this.editor.graph; + + if (graph.isEnabled()) + { + graph.getModel().beginUpdate(); + try + { + exec(); + } + catch (e) + { + throw e; + } + finally + { + // Animates the changes in the graph model except + // for Camino, where animation is too slow + if (this.allowAnimation && animate && (navigator.userAgent == null || + navigator.userAgent.indexOf('Camino') < 0)) + { + // New API for animating graph layout results asynchronously + var morph = new mxMorphing(graph); + morph.addListener(mxEvent.DONE, mxUtils.bind(this, function() + { + graph.getModel().endUpdate(); + + if (post != null) + { + post(); + } + })); + + morph.startAnimation(); + } + else + { + graph.getModel().endUpdate(); + + if (post != null) + { + post(); + } + } + } + } +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showImageDialog = function(title, value, fn, ignoreExisting) +{ + var cellEditor = this.editor.graph.cellEditor; + var selState = cellEditor.saveSelection(); + var newValue = mxUtils.prompt(title, value); + cellEditor.restoreSelection(selState); + + if (newValue != null && newValue.length > 0) + { + var img = new Image(); + + img.onload = function() + { + fn(newValue, img.width, img.height); + }; + img.onerror = function() + { + fn(null); + mxUtils.alert(mxResources.get('fileNotFound')); + }; + + img.src = newValue; + } + else + { + fn(null); + } +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showLinkDialog = function(value, btnLabel, fn) +{ + var dlg = new LinkDialog(this, value, btnLabel, fn); + this.showDialog(dlg.container, 420, 90, true, true); + dlg.init(); +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showDataDialog = function(cell) +{ + if (cell != null) + { + var dlg = new EditDataDialog(this, cell); + this.showDialog(dlg.container, 480, 420, true, false, null, false); + dlg.init(); + } +}; + +/** + * Hides the current menu. + */ + EditorUi.prototype.showSettingsDialog = function(cell) + { + if (cell != null) + { + var dlg = new EditSettingsDialog(this, cell); + this.showDialog(dlg.container, 480, 420, true, false, null, false); + dlg.init(); + } + }; + +/** + * Hides the current menu. + */ + EditorUi.prototype.showSettingsDialog = function(cell) + { + if (cell != null) + { + var dlg = new EditSettingsDialog(this, cell); + this.showDialog(dlg.container, 480, 420, true, false, null, false); + } + }; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showBackgroundImageDialog = function(apply) +{ + apply = (apply != null) ? apply : mxUtils.bind(this, function(image) + { + var change = new ChangePageSetup(this, null, image); + change.ignoreColor = true; + + this.editor.graph.model.execute(change); + }); + + var newValue = mxUtils.prompt(mxResources.get('backgroundImage'), ''); + + if (newValue != null && newValue.length > 0) + { + var img = new Image(); + + img.onload = function() + { + apply(new mxImage(newValue, img.width, img.height)); + }; + img.onerror = function() + { + apply(null); + mxUtils.alert(mxResources.get('fileNotFound')); + }; + + img.src = newValue; + } + else + { + apply(null); + } +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setBackgroundImage = function(image) +{ + this.editor.graph.setBackgroundImage(image); + this.editor.graph.view.validateBackgroundImage(); + + this.fireEvent(new mxEventObject('backgroundImageChanged')); +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.confirm = function(msg, okFn, cancelFn) +{ + if (mxUtils.confirm(msg)) + { + if (okFn != null) + { + okFn(); + } + } + else if (cancelFn != null) + { + cancelFn(); + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.createOutline = function(wnd) +{ + var outline = new mxOutline(this.editor.graph); + outline.border = 20; + + mxEvent.addListener(window, 'resize', function() + { + outline.update(); + }); + + this.addListener('pageFormatChanged', function() + { + outline.update(); + }); + + return outline; +}; + +// Alt+Shift+Keycode mapping to action +EditorUi.prototype.altShiftActions = {67: 'clearWaypoints', // Alt+Shift+C + 65: 'connectionArrows', // Alt+Shift+A + 76: 'editLink', // Alt+Shift+L + 80: 'connectionPoints', // Alt+Shift+P + 84: 'editTooltip', // Alt+Shift+T + 86: 'pasteSize', // Alt+Shift+V + 88: 'copySize' // Alt+Shift+X +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.createKeyHandler = function(editor) +{ + var editorUi = this; + var graph = this.editor.graph; + var keyHandler = new mxKeyHandler(graph); + + var isEventIgnored = keyHandler.isEventIgnored; + keyHandler.isEventIgnored = function(evt) + { + // Handles undo/redo/ctrl+./,/u via action and allows ctrl+b/i + // only if editing value is HTML (except for FF and Safari) + return !(mxEvent.isShiftDown(evt) && evt.keyCode == 9) && + ((!this.isControlDown(evt) || mxEvent.isShiftDown(evt) || + (evt.keyCode != 90 && evt.keyCode != 89 && evt.keyCode != 188 && + evt.keyCode != 190 && evt.keyCode != 85)) && ((evt.keyCode != 66 && evt.keyCode != 73) || + !this.isControlDown(evt) || (this.graph.cellEditor.isContentEditing() && + !mxClient.IS_FF && !mxClient.IS_SF)) && isEventIgnored.apply(this, arguments)); + }; + + // Ignores graph enabled state but not chromeless state + keyHandler.isEnabledForEvent = function(evt) + { + return (!mxEvent.isConsumed(evt) && this.isGraphEvent(evt) && this.isEnabled() && + (editorUi.dialogs == null || editorUi.dialogs.length == 0)); + }; + + // Routes command-key to control-key on Mac + keyHandler.isControlDown = function(evt) + { + return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey); + }; + + var queue = []; + var thread = null; + + // Helper function to move cells with the cursor keys + function nudge(keyCode, stepSize, resize) + { + queue.push(function() + { + if (!graph.isSelectionEmpty() && graph.isEnabled()) + { + stepSize = (stepSize != null) ? stepSize : 1; + + if (resize) + { + // Resizes all selected vertices + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i]) && graph.isCellResizable(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + + if (keyCode == 37) + { + geo.width = Math.max(0, geo.width - stepSize); + } + else if (keyCode == 38) + { + geo.height = Math.max(0, geo.height - stepSize); + } + else if (keyCode == 39) + { + geo.width += stepSize; + } + else if (keyCode == 40) + { + geo.height += stepSize; + } + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + else + { + // Moves vertices up/down in a stack layout + var cell = graph.getSelectionCell(); + var parent = graph.model.getParent(cell); + var layout = null; + + if (graph.getSelectionCount() == 1 && graph.model.isVertex(cell) && + graph.layoutManager != null && !graph.isCellLocked(cell)) + { + layout = graph.layoutManager.getLayout(parent); + } + + if (layout != null && layout.constructor == mxStackLayout) + { + var index = parent.getIndex(cell); + + if (keyCode == 37 || keyCode == 38) + { + graph.model.add(parent, cell, Math.max(0, index - 1)); + } + else if (keyCode == 39 ||keyCode == 40) + { + graph.model.add(parent, cell, Math.min(graph.model.getChildCount(parent), index + 1)); + } + } + else + { + var cells = graph.getMovableCells(graph.getSelectionCells()); + var realCells = []; + + for (var i = 0; i < cells.length; i++) + { + // TODO: Use getCompositeParent + var style = graph.getCurrentCellStyle(cells[i]); + + if (mxUtils.getValue(style, 'part', '0') == '1') + { + var parent = graph.model.getParent(cells[i]); + + if (graph.model.isVertex(parent) && mxUtils.indexOf(cells, parent) < 0) + { + realCells.push(parent); + } + } + else + { + realCells.push(cells[i]); + } + } + + if (realCells.length > 0) + { + cells = realCells; + var dx = 0; + var dy = 0; + + if (keyCode == 37) + { + dx = -stepSize; + } + else if (keyCode == 38) + { + dy = -stepSize; + } + else if (keyCode == 39) + { + dx = stepSize; + } + else if (keyCode == 40) + { + dy = stepSize; + } + + graph.moveCells(cells, dx, dy); + } + } + } + } + }); + + if (thread != null) + { + window.clearTimeout(thread); + } + + thread = window.setTimeout(function() + { + if (queue.length > 0) + { + graph.getModel().beginUpdate(); + + try + { + for (var i = 0; i < queue.length; i++) + { + queue[i](); + } + + queue = []; + } + finally + { + graph.getModel().endUpdate(); + } + } + }, 200); + }; + + // Overridden to handle special alt+shift+cursor keyboard shortcuts + var directions = {37: mxConstants.DIRECTION_WEST, 38: mxConstants.DIRECTION_NORTH, + 39: mxConstants.DIRECTION_EAST, 40: mxConstants.DIRECTION_SOUTH}; + + var keyHandlerGetFunction = keyHandler.getFunction; + + mxKeyHandler.prototype.getFunction = function(evt) + { + if (graph.isEnabled()) + { + // TODO: Add alt modified state in core API, here are some specific cases + if (mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) + { + var action = editorUi.actions.get(editorUi.altShiftActions[evt.keyCode]); + + if (action != null) + { + return action.funct; + } + } + + if (evt.keyCode == 9 && mxEvent.isAltDown(evt)) + { + if (graph.cellEditor.isContentEditing()) + { + // Alt+Shift+Tab while editing + return function() + { + document.execCommand('outdent', false, null); + }; + } + else if (mxEvent.isShiftDown(evt)) + { + // Alt+Shift+Tab + return function() + { + graph.selectParentCell(); + }; + } + else + { + // Alt+Tab + return function() + { + graph.selectChildCell(); + }; + } + } + else if (directions[evt.keyCode] != null && !graph.isSelectionEmpty()) + { + // On macOS, Control+Cursor is used by Expose so allow for Alt+Control to resize + if (!this.isControlDown(evt) && mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) + { + if (graph.model.isVertex(graph.getSelectionCell())) + { + return function() + { + var cells = graph.connectVertex(graph.getSelectionCell(), directions[evt.keyCode], + graph.defaultEdgeLength, evt, true); + + if (cells != null && cells.length > 0) + { + if (cells.length == 1 && graph.model.isEdge(cells[0])) + { + graph.setSelectionCell(graph.model.getTerminal(cells[0], false)); + } + else + { + graph.setSelectionCell(cells[cells.length - 1]); + } + + graph.scrollCellToVisible(graph.getSelectionCell()); + + if (editorUi.hoverIcons != null) + { + editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); + } + } + }; + } + } + else + { + // Avoids consuming event if no vertex is selected by returning null below + // Cursor keys move and resize (ctrl) cells + if (this.isControlDown(evt)) + { + return function() + { + nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null, true); + }; + } + else + { + return function() + { + nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null); + }; + } + } + } + } + + return keyHandlerGetFunction.apply(this, arguments); + }; + + // Binds keystrokes to actions + keyHandler.bindAction = mxUtils.bind(this, function(code, control, key, shift) + { + var action = this.actions.get(key); + + if (action != null) + { + var f = function() + { + if (action.isEnabled()) + { + action.funct(); + } + }; + + if (control) + { + if (shift) + { + keyHandler.bindControlShiftKey(code, f); + } + else + { + keyHandler.bindControlKey(code, f); + } + } + else + { + if (shift) + { + keyHandler.bindShiftKey(code, f); + } + else + { + keyHandler.bindKey(code, f); + } + } + } + }); + + var ui = this; + var keyHandlerEscape = keyHandler.escape; + keyHandler.escape = function(evt) + { + keyHandlerEscape.apply(this, arguments); + }; + + // Ignores enter keystroke. Remove this line if you want the + // enter keystroke to stop editing. N, W, T are reserved. + keyHandler.enter = function() {}; + + keyHandler.bindControlShiftKey(36, function() { graph.exitGroup(); }); // Ctrl+Shift+Home + keyHandler.bindControlShiftKey(35, function() { graph.enterGroup(); }); // Ctrl+Shift+End + keyHandler.bindKey(36, function() { graph.home(); }); // Home + keyHandler.bindKey(35, function() { graph.refresh(); }); // End + keyHandler.bindAction(107, true, 'zoomIn'); // Ctrl+Plus + keyHandler.bindAction(109, true, 'zoomOut'); // Ctrl+Minus + keyHandler.bindAction(80, true, 'print'); // Ctrl+P + keyHandler.bindAction(79, true, 'outline', true); // Ctrl+Shift+O + + if (!this.editor.chromeless || this.editor.editable) + { + keyHandler.bindControlKey(36, function() { if (graph.isEnabled()) { graph.foldCells(true); }}); // Ctrl+Home + keyHandler.bindControlKey(35, function() { if (graph.isEnabled()) { graph.foldCells(false); }}); // Ctrl+End + keyHandler.bindControlKey(13, function() + { + if (graph.isEnabled()) + { + try + { + graph.setSelectionCells(graph.duplicateCells(graph.getSelectionCells(), false)); + } + catch (e) + { + ui.handleError(e); + } + } + }); // Ctrl+Enter + keyHandler.bindAction(8, false, 'delete'); // Backspace + keyHandler.bindAction(8, true, 'deleteAll'); // Backspace + keyHandler.bindAction(46, false, 'delete'); // Delete + keyHandler.bindAction(46, true, 'deleteAll'); // Ctrl+Delete + keyHandler.bindAction(72, true, 'resetView'); // Ctrl+H + keyHandler.bindAction(72, true, 'fitWindow', true); // Ctrl+Shift+H + keyHandler.bindAction(74, true, 'fitPage'); // Ctrl+J + keyHandler.bindAction(74, true, 'fitTwoPages', true); // Ctrl+Shift+J + keyHandler.bindAction(48, true, 'customZoom'); // Ctrl+0 + keyHandler.bindAction(82, true, 'turn'); // Ctrl+R + keyHandler.bindAction(82, true, 'clearDefaultStyle', true); // Ctrl+Shift+R + keyHandler.bindAction(83, true, 'save'); // Ctrl+S + keyHandler.bindAction(83, true, 'saveAs', true); // Ctrl+Shift+S + keyHandler.bindAction(65, true, 'selectAll'); // Ctrl+A + keyHandler.bindAction(65, true, 'selectNone', true); // Ctrl+A + keyHandler.bindAction(73, true, 'selectVertices', true); // Ctrl+Shift+I + keyHandler.bindAction(69, true, 'selectEdges', true); // Ctrl+Shift+E + keyHandler.bindAction(69, true, 'editStyle'); // Ctrl+E + keyHandler.bindAction(66, true, 'bold'); // Ctrl+B + keyHandler.bindAction(66, true, 'toBack', true); // Ctrl+Shift+B + keyHandler.bindAction(70, true, 'toFront', true); // Ctrl+Shift+F + keyHandler.bindAction(68, true, 'duplicate'); // Ctrl+D + keyHandler.bindAction(68, true, 'setAsDefaultStyle', true); // Ctrl+Shift+D + keyHandler.bindAction(90, true, 'undo'); // Ctrl+Z + keyHandler.bindAction(89, true, 'autosize', true); // Ctrl+Shift+Y + keyHandler.bindAction(88, true, 'cut'); // Ctrl+X + keyHandler.bindAction(67, true, 'copy'); // Ctrl+C + keyHandler.bindAction(86, true, 'paste'); // Ctrl+V + keyHandler.bindAction(71, true, 'group'); // Ctrl+G + keyHandler.bindAction(77, true, 'editData'); // Ctrl+M + keyHandler.bindAction(71, true, 'grid', true); // Ctrl+Shift+G + keyHandler.bindAction(73, true, 'italic'); // Ctrl+I + keyHandler.bindAction(76, true, 'lockUnlock'); // Ctrl+L + keyHandler.bindAction(76, true, 'layers', true); // Ctrl+Shift+L + keyHandler.bindAction(80, true, 'formatPanel', true); // Ctrl+Shift+P + keyHandler.bindAction(85, true, 'underline'); // Ctrl+U + keyHandler.bindAction(85, true, 'ungroup', true); // Ctrl+Shift+U + keyHandler.bindAction(190, true, 'superscript'); // Ctrl+. + keyHandler.bindAction(188, true, 'subscript'); // Ctrl+, + keyHandler.bindAction(9, false, 'indent', true); // Shift+Tab, + keyHandler.bindKey(13, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // Enter + keyHandler.bindKey(113, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // F2 + } + + if (!mxClient.IS_WIN) + { + keyHandler.bindAction(90, true, 'redo', true); // Ctrl+Shift+Z + } + else + { + keyHandler.bindAction(89, true, 'redo'); // Ctrl+Y + } + + return keyHandler; +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.destroy = function() +{ + if (this.editor != null) + { + this.editor.destroy(); + this.editor = null; + } + + if (this.menubar != null) + { + this.menubar.destroy(); + this.menubar = null; + } + + if (this.toolbar != null) + { + this.toolbar.destroy(); + this.toolbar = null; + } + + if (this.sidebar != null) + { + this.sidebar.destroy(); + this.sidebar = null; + } + + if (this.keyHandler != null) + { + this.keyHandler.destroy(); + this.keyHandler = null; + } + + if (this.keydownHandler != null) + { + mxEvent.removeListener(document, 'keydown', this.keydownHandler); + this.keydownHandler = null; + } + + if (this.keyupHandler != null) + { + mxEvent.removeListener(document, 'keyup', this.keyupHandler); + this.keyupHandler = null; + } + + if (this.resizeHandler != null) + { + mxEvent.removeListener(window, 'resize', this.resizeHandler); + this.resizeHandler = null; + } + + if (this.gestureHandler != null) + { + mxEvent.removeGestureListeners(document, this.gestureHandler); + this.gestureHandler = null; + } + + if (this.orientationChangeHandler != null) + { + mxEvent.removeListener(window, 'orientationchange', this.orientationChangeHandler); + this.orientationChangeHandler = null; + } + + if (this.scrollHandler != null) + { + mxEvent.removeListener(window, 'scroll', this.scrollHandler); + this.scrollHandler = null; + } + + if (this.destroyFunctions != null) + { + for (var i = 0; i < this.destroyFunctions.length; i++) + { + this.destroyFunctions[i](); + } + + this.destroyFunctions = null; + } + + var c = [this.menubarContainer, this.toolbarContainer, this.sidebarContainer, + this.formatContainer, this.diagramContainer, this.footerContainer, + this.chromelessToolbar, this.hsplit, this.sidebarFooterContainer, + this.layersDialog]; + + for (var i = 0; i < c.length; i++) + { + if (c[i] != null && c[i].parentNode != null) + { + c[i].parentNode.removeChild(c[i]); + } + } +}; diff --git a/static/mxgraph/examples/grapheditor/www/js/Format.js b/static/mxgraph/examples/grapheditor/www/js/Format.js new file mode 100644 index 0000000..d21e822 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/Format.js @@ -0,0 +1,5817 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +Format = function(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.labelIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.currentIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.showCloseButton = true; + +/** + * Background color for inactive tabs. + */ +Format.prototype.inactiveTabBackgroundColor = '#f1f3f4'; + +/** + * Background color for inactive tabs. + */ +Format.prototype.roundableShapes = ['label', 'rectangle', 'internalStorage', 'corner', + 'parallelogram', 'swimlane', 'triangle', 'trapezoid', + 'ext', 'step', 'tee', 'process', 'link', + 'rhombus', 'offPageConnector', 'loopLimit', 'hexagon', + 'manualInput', 'curlyBracket', 'singleArrow', 'callout', + 'doubleArrow', 'flexArrow', 'card', 'umlLifeline']; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + this.update = mxUtils.bind(this, function(sender, evt) + { + this.clearSelectionState(); + this.refresh(); + }); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.EDITING_STARTED, this.update); + graph.addListener(mxEvent.EDITING_STOPPED, this.update); + graph.getModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.ROOT, mxUtils.bind(this, function() + { + this.refresh(); + })); + + editor.addListener('autosaveChanged', mxUtils.bind(this, function() + { + this.refresh(); + })); + + this.refresh(); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.clearSelectionState = function() +{ + this.selectionState = null; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.getSelectionState = function() +{ + if (this.selectionState == null) + { + this.selectionState = this.createSelectionState(); + } + + return this.selectionState; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.createSelectionState = function() +{ + var cells = this.editorUi.editor.graph.getSelectionCells(); + var result = this.initSelectionState(); + + for (var i = 0; i < cells.length; i++) + { + this.updateSelectionStateForCell(result, cells[i], cells); + } + + return result; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.initSelectionState = function() +{ + return {vertices: [], edges: [], x: null, y: null, width: null, height: null, style: {}, + containsImage: false, containsLabel: false, fill: true, glass: true, rounded: true, + comic: true, autoSize: false, image: true, shadow: true, lineJumps: true}; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.updateSelectionStateForCell = function(result, cell, cells) +{ + var graph = this.editorUi.editor.graph; + + if (graph.getModel().isVertex(cell)) + { + result.vertices.push(cell); + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + if (geo.width > 0) + { + if (result.width == null) + { + result.width = geo.width; + } + else if (result.width != geo.width) + { + result.width = ''; + } + } + else + { + result.containsLabel = true; + } + + if (geo.height > 0) + { + if (result.height == null) + { + result.height = geo.height; + } + else if (result.height != geo.height) + { + result.height = ''; + } + } + else + { + result.containsLabel = true; + } + + if (!geo.relative || geo.offset != null) + { + var x = (geo.relative) ? geo.offset.x : geo.x; + var y = (geo.relative) ? geo.offset.y : geo.y; + + if (result.x == null) + { + result.x = x; + } + else if (result.x != x) + { + result.x = ''; + } + + if (result.y == null) + { + result.y = y; + } + else if (result.y != y) + { + result.y = ''; + } + } + } + } + else if (graph.getModel().isEdge(cell)) + { + result.edges.push(cell); + } + + var state = graph.view.getState(cell); + + if (state != null) + { + result.autoSize = result.autoSize || this.isAutoSizeState(state); + result.glass = result.glass && this.isGlassState(state); + result.rounded = result.rounded && this.isRoundedState(state); + result.lineJumps = result.lineJumps && this.isLineJumpState(state); + result.comic = result.comic && this.isComicState(state); + result.image = result.image && this.isImageState(state); + result.shadow = result.shadow && this.isShadowState(state); + result.fill = result.fill && this.isFillState(state); + + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + result.containsImage = result.containsImage || shape == 'image'; + + for (var key in state.style) + { + var value = state.style[key]; + + if (value != null) + { + if (result.style[key] == null) + { + result.style[key] = value; + } + else if (result.style[key] != value) + { + result.style[key] = ''; + } + } + } + } +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isFillState = function(state) +{ + return state.view.graph.model.isVertex(state.cell) || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'arrow' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'filledEdge' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'flexArrow'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isGlassState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'rectangle' || shape == 'internalStorage' || + shape == 'ext' || shape == 'umlLifeline' || shape == 'swimlane' || + shape == 'process'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isRoundedState = function(state) +{ + return (state.shape != null) ? state.shape.isRoundable() : + mxUtils.indexOf(this.roundableShapes, mxUtils.getValue(state.style, + mxConstants.STYLE_SHAPE, null)) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isLineJumpState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + var curved = mxUtils.getValue(state.style, mxConstants.STYLE_CURVED, false); + + return !curved && (shape == 'connector' || shape == 'filledEdge'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isComicState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return mxUtils.indexOf(['label', 'rectangle', 'internalStorage', 'corner', 'parallelogram', 'note', 'collate', + 'swimlane', 'triangle', 'trapezoid', 'ext', 'step', 'tee', 'process', 'link', 'rhombus', + 'offPageConnector', 'loopLimit', 'hexagon', 'manualInput', 'singleArrow', 'doubleArrow', + 'flexArrow', 'filledEdge', 'card', 'umlLifeline', 'connector', 'folder', 'component', 'sortShape', + 'cross', 'umlFrame', 'cube', 'isoCube', 'isoRectangle', 'partialRectangle'], shape) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isAutoSizeState = function(state) +{ + return mxUtils.getValue(state.style, mxConstants.STYLE_AUTOSIZE, null) == '1'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isImageState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'image'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isShadowState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape != 'image'); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.clear = function() +{ + this.container.innerHTML = ''; + + // Destroy existing panels + if (this.panels != null) + { + for (var i = 0; i < this.panels.length; i++) + { + this.panels[i].destroy(); + } + } + + this.panels = []; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.refresh = function() +{ + // Performance tweak: No refresh needed if not visible + if (this.container.style.width == '0px') + { + return; + } + + this.clear(); + var ui = this.editorUi; + var graph = ui.editor.graph; + + var div = document.createElement('div'); + div.style.whiteSpace = 'nowrap'; + div.style.color = 'rgb(112, 112, 112)'; + div.style.textAlign = 'left'; + div.style.cursor = 'default'; + + var label = document.createElement('div'); + label.className = 'geFormatSection'; + label.style.textAlign = 'center'; + label.style.fontWeight = 'bold'; + label.style.paddingTop = '8px'; + label.style.fontSize = '13px'; + label.style.borderWidth = '0px 0px 1px 1px'; + label.style.borderStyle = 'solid'; + label.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + label.style.height = (mxClient.IS_QUIRKS) ? '34px' : '25px'; + label.style.overflow = 'hidden'; + label.style.width = '100%'; + this.container.appendChild(div); + + // Prevents text selection + mxEvent.addListener(label, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', + mxUtils.bind(this, function(evt) + { + evt.preventDefault(); + })); + + mxUtils.write(label, mxResources.get('properties')); + label.style.borderLeftWidth = '0px'; + + // Adds button to hide the format panel since + // people don't seem to find the toolbar button + // and the menu item in the format menu + if (this.showCloseButton) + { + var img = document.createElement('img'); + img.setAttribute('border', '0'); + img.setAttribute('src', Dialog.prototype.closeImage); + img.setAttribute('title', mxResources.get('hide')); + img.style.position = 'absolute'; + img.style.display = 'block'; + img.style.right = '0px'; + img.style.top = '8px'; + img.style.cursor = 'pointer'; + img.style.marginTop = '1px'; + img.style.marginRight = '17px'; + img.style.border = '1px solid transparent'; + img.style.padding = '1px'; + img.style.opacity = 0.5; + label.appendChild(img) + + mxEvent.addListener(img, 'click', function() + { + ui.actions.get('formatPanel').funct(); + }); + } + + div.appendChild(label); + this.panels.push(new DiagramFormatPanel(this, ui, div)); + + +}; + +/** + * Base class for format panels. + */ +BaseFormatPanel = function(format, editorUi, container) +{ + this.format = format; + this.editorUi = editorUi; + this.container = container; + this.listeners = []; +}; + +/** + * + */ +BaseFormatPanel.prototype.buttonBackgroundColor = 'white'; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.getSelectionState = function() +{ + var graph = this.editorUi.editor.graph; + var cells = graph.getSelectionCells(); + var shape = null; + + for (var i = 0; i < cells.length; i++) + { + var state = graph.view.getState(cells[i]); + + if (state != null) + { + var tmp = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + if (tmp != null) + { + if (shape == null) + { + shape = tmp; + } + else if (shape != tmp) + { + return null; + } + } + + } + } + + return shape; +}; + +/** + * Install input handler. + */ +BaseFormatPanel.prototype.installInputHandler = function(input, key, defaultValue, min, max, unit, textEditFallback, isFloat) +{ + unit = (unit != null) ? unit : ''; + isFloat = (isFloat != null) ? isFloat : false; + + var ui = this.editorUi; + var graph = ui.editor.graph; + + min = (min != null) ? min : 1; + max = (max != null) ? max : 999; + + var selState = null; + var updating = false; + + var update = mxUtils.bind(this, function(evt) + { + var value = (isFloat) ? parseFloat(input.value) : parseInt(input.value); + + // Special case: angle mod 360 + if (!isNaN(value) && key == mxConstants.STYLE_ROTATION) + { + // Workaround for decimal rounding errors in floats is to + // use integer and round all numbers to two decimal point + value = mxUtils.mod(Math.round(value * 100), 36000) / 100; + } + + value = Math.min(max, Math.max(min, (isNaN(value)) ? defaultValue : value)); + + if (graph.cellEditor.isContentEditing() && textEditFallback) + { + if (!updating) + { + updating = true; + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + textEditFallback(value); + input.value = value + unit; + + // Restore focus and selection in input + updating = false; + } + } + else if (value != mxUtils.getValue(this.format.getSelectionState().style, key, defaultValue)) + { + if (graph.isEditing()) + { + graph.stopEditing(true); + } + + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + graph.setCellStyles(key, value, cells); + + // Handles special case for fontSize where HTML labels are parsed and updated + if (key == mxConstants.STYLE_FONTSIZE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontSize = value + 'px'; + elt.removeAttribute('size'); + }); + } + + for (var i = 0; i < cells.length; i++) + { + if (graph.model.getChildCount(cells[i]) == 0) + { + graph.autoSizeCell(cells[i], false); + } + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', cells)); + } + finally + { + graph.getModel().endUpdate(); + } + } + + input.value = value + unit; + mxEvent.consume(evt); + }); + + if (textEditFallback && graph.cellEditor.isContentEditing()) + { + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + } + + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'blur', update); + + return update; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createPanel = function() +{ + var div = document.createElement('div'); + div.className = 'geFormatSection'; + div.style.padding = '12px 0px 12px 18px'; + + + return div; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createTitle = function(title) +{ + var div = document.createElement('div'); + div.style.padding = '0px 0px 6px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.fontWeight = 'bold'; + mxUtils.write(div, title); + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createStepper = function(input, update, step, height, disableFocus, defaultValue, isFloat) +{ + step = (step != null) ? step : 1; + height = (height != null) ? height : 8; + + if (mxClient.IS_QUIRKS) + { + height = height - 2; + } + else if (mxClient.IS_MT || document.documentMode >= 8) + { + height = height + 1; + } + + var stepper = document.createElement('div'); + mxUtils.setPrefixedStyle(stepper.style, 'borderRadius', '3px'); + stepper.style.border = '1px solid rgb(192, 192, 192)'; + stepper.style.position = 'absolute'; + + var up = document.createElement('div'); + up.style.borderBottom = '1px solid rgb(192, 192, 192)'; + up.style.position = 'relative'; + up.style.height = height + 'px'; + up.style.width = '10px'; + up.className = 'geBtnUp'; + stepper.appendChild(up); + + var down = up.cloneNode(false); + down.style.border = 'none'; + down.style.height = height + 'px'; + down.className = 'geBtnDown'; + stepper.appendChild(down); + + mxEvent.addListener(down, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '2'; + } + + var val = isFloat? parseFloat(input.value) : parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val - step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + mxEvent.addListener(up, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '0'; + } + + var val = isFloat? parseFloat(input.value) : parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val + step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + // Disables transfer of focus to DIV but also :active CSS + // so it's only used for fontSize where the focus should + // stay on the selected text, but not for any other input. + if (disableFocus) + { + var currentSelection = null; + + mxEvent.addGestureListeners(stepper, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + currentSelection = document.selection.createRange(); + } + + mxEvent.consume(evt); + }, + null, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (currentSelection != null) + { + try + { + currentSelection.select(); + } + catch (e) + { + // ignore + } + + currentSelection = null; + mxEvent.consume(evt); + } + } + ); + } + + return stepper; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createOption = function(label, isCheckedFn, setCheckedFn, listener) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + div.appendChild(cb); + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var applying = false; + var value = isCheckedFn(); + + var apply = function(newValue) + { + if (!applying) + { + applying = true; + + if (newValue) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + if (value != newValue) + { + value = newValue; + + // Checks if the color value needs to be updated in the model + if (isCheckedFn() != value) + { + setCheckedFn(value); + } + } + + applying = false; + } + }; + + mxEvent.addListener(div, 'click', function(evt) + { + if (cb.getAttribute('disabled') != 'disabled') + { + // Toggles checkbox state for click on label + var source = mxEvent.getSource(evt); + + if (source == div || source == span) + { + cb.checked = !cb.checked; + } + + apply(cb.checked); + } + }); + + apply(value); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * The string 'null' means use null in values. + */ +BaseFormatPanel.prototype.createCellOption = function(label, key, defaultValue, enabledValue, disabledValue, fn, action, stopEditing) +{ + enabledValue = (enabledValue != null) ? ((enabledValue == 'null') ? null : enabledValue) : '1'; + disabledValue = (disabledValue != null) ? ((disabledValue == 'null') ? null : disabledValue) : '0'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, key, defaultValue) != disabledValue; + } + + return null; + }, function(checked) + { + if (stopEditing) + { + graph.stopEditing(); + } + + if (action != null) + { + action.funct(); + } + else + { + graph.getModel().beginUpdate(); + try + { + var value = (checked) ? enabledValue : disabledValue; + graph.setCellStyles(key, value, graph.getSelectionCells()); + + if (fn != null) + { + fn(graph.getSelectionCells(), value); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, key, defaultValue) != disabledValue); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }); +}; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setColorFn, defaultColor, listener, callbackFn, hideCheckbox) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + + if (!hideCheckbox) + { + div.appendChild(cb); + } + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var value = getColorFn(); + var applying = false; + var btn = null; + + var apply = function(color, disableUpdate, forceUpdate) + { + if (!applying) + { + applying = true; + color = (/(^#?[a-zA-Z0-9]*$)/.test(color)) ? color : defaultColor; + btn.innerHTML = '
'; + + // Fine-tuning in Firefox, quirks mode and IE8 standards + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + btn.firstChild.style.margin = '0px'; + } + + if (color != null && color != mxConstants.NONE) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + + if (callbackFn != null) + { + callbackFn(color); + } + + if (!disableUpdate) + { + value = color; + + // Checks if the color value needs to be updated in the model + if (forceUpdate || hideCheckbox || getColorFn() != value) + { + setColorFn(value); + } + } + + applying = false; + } + }; + + btn = mxUtils.button('', mxUtils.bind(this, function(evt) + { + this.editorUi.pickColor(value, function(color) + { + apply(color, null, true); + }); + mxEvent.consume(evt); + })); + + btn.style.position = 'absolute'; + btn.style.marginTop = '-4px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '0px' : '20px'; + btn.style.height = '22px'; + btn.className = 'geColorBtn'; + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + div.appendChild(btn); + + mxEvent.addListener(div, 'click', function(evt) + { + var source = mxEvent.getSource(evt); + + if (source == cb || source.nodeName != 'INPUT') + { + // Toggles checkbox state for click on label + if (source != cb) + { + cb.checked = !cb.checked; + } + + // Overrides default value with current value to make it easier + // to restore previous value if the checkbox is clicked twice + if (!cb.checked && value != null && value != mxConstants.NONE && + defaultColor != mxConstants.NONE) + { + defaultColor = value; + } + + apply((cb.checked) ? defaultColor : mxConstants.NONE); + } + }); + + apply(value, true); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createCellColorOption = function(label, colorKey, defaultColor, callbackFn, setStyleFn) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createColorOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, colorKey, null); + } + + return null; + }, function(color) + { + graph.getModel().beginUpdate(); + try + { + if (setStyleFn != null) + { + setStyleFn(color); + } + + graph.setCellStyles(colorKey, color, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [colorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }, defaultColor || mxConstants.NONE, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, colorKey, null)); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }, callbackFn); +}; + +/** + * + */ +BaseFormatPanel.prototype.addArrow = function(elt, height) +{ + height = (height != null) ? height : 10; + + var arrow = document.createElement('div'); + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + arrow.style.padding = '6px'; + arrow.style.paddingRight = '4px'; + + var m = (10 - height); + + if (m == 2) + { + arrow.style.paddingTop = 6 + 'px'; + } + else if (m > 0) + { + arrow.style.paddingTop = (6 - m) + 'px'; + } + else + { + arrow.style.marginTop = '-2px'; + } + + arrow.style.height = height + 'px'; + arrow.style.borderLeft = '1px solid #a0a0a0'; + arrow.innerHTML = ''; + mxUtils.setOpacity(arrow, 70); + + var symbol = elt.getElementsByTagName('div')[0]; + + if (symbol != null) + { + symbol.style.paddingRight = '6px'; + symbol.style.marginLeft = '4px'; + symbol.style.marginTop = '-1px'; + symbol.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + mxUtils.setOpacity(symbol, 60); + } + + mxUtils.setOpacity(elt, 100); + elt.style.border = '1px solid #a0a0a0'; + elt.style.backgroundColor = this.buttonBackgroundColor; + elt.style.backgroundImage = 'none'; + elt.style.width = 'auto'; + elt.className += ' geColorBtn'; + mxUtils.setPrefixedStyle(elt.style, 'borderRadius', '3px'); + + elt.appendChild(arrow); + + return symbol; +}; + +/** + * + */ +BaseFormatPanel.prototype.addUnitInput = function(container, unit, right, width, update, step, marginTop, disableFocus, isFloat) +{ + marginTop = (marginTop != null) ? marginTop : 0; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.marginTop = '-2px'; + input.style.right = (right + 12) + 'px'; + input.style.width = width + 'px'; + container.appendChild(input); + + var stepper = this.createStepper(input, update, step, null, disableFocus, null, isFloat); + stepper.style.marginTop = (marginTop - 2) + 'px'; + stepper.style.right = right + 'px'; + container.appendChild(stepper); + + return input; +}; + +/** + * + */ +BaseFormatPanel.prototype.createRelativeOption = function(label, key, width, handler, init) +{ + width = (width != null) ? width : 44; + + var graph = this.editorUi.editor.graph; + var div = this.createPanel(); + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + mxUtils.write(div, label); + div.style.fontWeight = 'bold'; + + var update = mxUtils.bind(this, function(evt) + { + if (handler != null) + { + handler(input); + } + else + { + var value = parseInt(input.value); + value = Math.min(100, Math.max(0, (isNaN(value)) ? 100 : value)); + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null && value != mxUtils.getValue(state.style, key, 100)) + { + // Removes entry in style (assumes 100 is default for relative values) + if (value == 100) + { + value = null; + } + + graph.setCellStyles(key, value, graph.getSelectionCells()); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = ((value != null) ? value : '100') + ' %'; + } + + mxEvent.consume(evt); + }); + + var input = this.addUnitInput(div, '%', 20, width, update, 10, -15, handler != null); + + if (key != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || input != document.activeElement) + { + var ss = this.format.getSelectionState(); + var tmp = parseInt(mxUtils.getValue(ss.style, key, 100)); + input.value = (isNaN(tmp)) ? '' : tmp + ' %'; + } + }); + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + listener(null, null, true); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + if (init != null) + { + init(input); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.addLabel = function(div, title, right, width) +{ + width = (width != null) ? width : 61; + + var label = document.createElement('div'); + mxUtils.write(label, title); + label.style.position = 'absolute'; + label.style.right = right + 'px'; + label.style.width = width + 'px'; + label.style.marginTop = '6px'; + label.style.textAlign = 'center'; + div.appendChild(label); +}; + +/** + * + */ +BaseFormatPanel.prototype.addKeyHandler = function(input, listener) +{ + mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e) + { + if (e.keyCode == 13) + { + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + if (listener != null) + { + listener(null, null, true); + } + + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + })); +}; + +/** + * + */ +BaseFormatPanel.prototype.styleButtons = function(elts) +{ + for (var i = 0; i < elts.length; i++) + { + mxUtils.setPrefixedStyle(elts[i].style, 'borderRadius', '3px'); + mxUtils.setOpacity(elts[i], 100); + elts[i].style.border = '1px solid #a0a0a0'; + elts[i].style.padding = '4px'; + elts[i].style.paddingTop = '3px'; + elts[i].style.paddingRight = '1px'; + elts[i].style.margin = '1px'; + elts[i].style.width = '24px'; + elts[i].style.height = '20px'; + elts[i].className += ' geColorBtn'; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +BaseFormatPanel.prototype.destroy = function() +{ + if (this.listeners != null) + { + for (var i = 0; i < this.listeners.length; i++) + { + this.listeners[i].destroy(); + } + + this.listeners = null; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(ArrangePanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel.prototype.init = function() +{ + var graph = this.editorUi.editor.graph; + var ss = this.format.getSelectionState(); + + this.container.appendChild(this.addLayerOps(this.createPanel())); + // Special case that adds two panels + this.addGeometry(this.container); + this.addEdgeGeometry(this.container); + + if (!ss.containsLabel || ss.edges.length == 0) + { + this.container.appendChild(this.addAngle(this.createPanel())); + } + + if (!ss.containsLabel && ss.edges.length == 0 && + ss.style.shape != 'rectangle' && + ss.style.shape != 'label') + { + this.container.appendChild(this.addFlip(this.createPanel())); + } + + if (ss.vertices.length > 1) + { + this.container.appendChild(this.addAlign(this.createPanel())); + this.container.appendChild(this.addDistribute(this.createPanel())); + } + else if (ss.vertices.length == 1 && ss.edges.length == 0 && + (graph.isTable(ss.vertices[0]) || + graph.isTableRow(ss.vertices[0]) || + graph.isTableCell(ss.vertices[0]))) + { + this.container.appendChild(this.addTable(this.createPanel())); + } + + this.container.appendChild(this.addGroupOps(this.createPanel())); + + if (ss.containsLabel) + { + // Adds functions from hidden style format panel + var span = document.createElement('div'); + span.style.width = '100%'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + span.style.padding = '10px 0 0 18px'; + mxUtils.write(span, mxResources.get('style')); + this.container.appendChild(span); + + new StyleFormatPanel(this.format, this.editorUi, this.container); + } +}; + +/** + * + */ +ArrangePanel.prototype.addTable = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + div.style.paddingTop = '6px'; + div.style.paddingBottom = '10px'; + + var span = document.createElement('div'); + span.style.marginTop = '2px'; + span.style.marginBottom = '8px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('table')); + div.appendChild(span); + + var panel = document.createElement('div'); + panel.style.position = 'relative'; + panel.style.paddingLeft = '0px'; + panel.style.borderWidth = '0px'; + panel.className = 'geToolbarContainer'; + + var btns = [ + ui.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'), + mxUtils.bind(this, function() + { + try + { + graph.insertTableColumn(ss.vertices[0], true); + } + catch (e) + { + ui.handleError(e); + } + }), panel), + ui.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'), + mxUtils.bind(this, function() + { + try + { + graph.insertTableColumn(ss.vertices[0], false); + } + catch (e) + { + ui.handleError(e); + } + }), panel), + ui.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'), + mxUtils.bind(this, function() + { + try + { + graph.deleteTableColumn(ss.vertices[0]); + } + catch (e) + { + ui.handleError(e); + } + }), panel), + ui.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'), + mxUtils.bind(this, function() + { + try + { + graph.insertTableRow(ss.vertices[0], true); + } + catch (e) + { + ui.handleError(e); + } + }), panel), + ui.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'), + mxUtils.bind(this, function() + { + try + { + graph.insertTableRow(ss.vertices[0], false); + } + catch (e) + { + ui.handleError(e); + } + }), panel), + ui.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'), + mxUtils.bind(this, function() + { + try + { + graph.deleteTableRow(ss.vertices[0]); + } + catch (e) + { + ui.handleError(e); + } + }), panel)]; + this.styleButtons(btns); + div.appendChild(panel); + btns[2].style.marginRight = '9px'; + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addLayerOps = function(div) +{ + var ui = this.editorUi; + + var btn = mxUtils.button(mxResources.get('toFront'), function(evt) + { + ui.actions.get('toFront').funct(); + }) + + btn.setAttribute('title', mxResources.get('toFront') + ' (' + this.editorUi.actions.get('toFront').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('toBack'), function(evt) + { + ui.actions.get('toBack').funct(); + }) + + btn.setAttribute('title', mxResources.get('toBack') + ' (' + this.editorUi.actions.get('toBack').shortcut + ')'); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addGroupOps = function(div) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var cell = graph.getSelectionCell(); + var ss = this.format.getSelectionState(); + var count = 0; + var btn = null; + + div.style.paddingTop = '8px'; + div.style.paddingBottom = '6px'; + + if (graph.getSelectionCount() > 1) + { + btn = mxUtils.button(mxResources.get('group'), function(evt) + { + ui.actions.get('group').funct(); + }) + + btn.setAttribute('title', mxResources.get('group') + ' (' + this.editorUi.actions.get('group').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && + graph.getModel().getChildCount(cell) > 0) + { + btn = mxUtils.button(mxResources.get('ungroup'), function(evt) + { + ui.actions.get('ungroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('ungroup') + ' (' + + this.editorUi.actions.get('ungroup').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (ss.vertices.length > 0) + { + if (count > 0) + { + mxUtils.br(div); + count = 0; + } + + var btn = mxUtils.button(mxResources.get('copySize'), function(evt) + { + ui.actions.get('copySize').funct(); + }); + + btn.setAttribute('title', mxResources.get('copySize') + ' (' + + this.editorUi.actions.get('copySize').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + count++; + + if (ui.copiedSize != null) + { + var btn2 = mxUtils.button(mxResources.get('pasteSize'), function(evt) + { + ui.actions.get('pasteSize').funct(); + }); + + btn2.setAttribute('title', mxResources.get('pasteSize') + ' (' + + this.editorUi.actions.get('pasteSize').shortcut + ')'); + + div.appendChild(btn2); + count++; + + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + btn2.style.width = '100px'; + btn2.style.marginBottom = '2px'; + } + } + + if (graph.getSelectionCount() == 1 && graph.getModel().isVertex(cell) && + graph.getModel().isVertex(graph.getModel().getParent(cell))) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('removeFromGroup'), function(evt) + { + ui.actions.get('removeFromGroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('removeFromGroup')); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() > 0) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('clearWaypoints'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearWaypoints').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearWaypoints') + ' (' + this.editorUi.actions.get('clearWaypoints').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + count++; + } + + if (graph.getSelectionCount() == 1) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + + btn = mxUtils.button(mxResources.get('editLink'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editLink').funct(); + })); + + btn.setAttribute('title', mxResources.get('editLink')); + btn.style.width = '100px'; + btn.style.marginLeft = '2px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (count == 0) + { + div.style.display = 'none'; + } + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAlign = function(div) +{ + var graph = this.editorUi.editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + div.appendChild(this.createTitle(mxResources.get('align'))); + + var stylePanel = document.createElement('div'); + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '0px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + div.style.height = '60px'; + } + + var left = this.editorUi.toolbar.addButton('geSprite-alignleft', mxResources.get('left'), + function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, stylePanel); + var center = this.editorUi.toolbar.addButton('geSprite-aligncenter', mxResources.get('center'), + function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, stylePanel); + var right = this.editorUi.toolbar.addButton('geSprite-alignright', mxResources.get('right'), + function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, stylePanel); + + var top = this.editorUi.toolbar.addButton('geSprite-aligntop', mxResources.get('top'), + function() { graph.alignCells(mxConstants.ALIGN_TOP); }, stylePanel); + var middle = this.editorUi.toolbar.addButton('geSprite-alignmiddle', mxResources.get('middle'), + function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, stylePanel); + var bottom = this.editorUi.toolbar.addButton('geSprite-alignbottom', mxResources.get('bottom'), + function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, stylePanel); + + this.styleButtons([left, center, right, top, middle, bottom]); + right.style.marginRight = '6px'; + div.appendChild(stylePanel); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addFlip = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '10px'; + + var span = document.createElement('div'); + span.style.marginTop = '2px'; + span.style.marginBottom = '8px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('flip')); + div.appendChild(span); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addDistribute = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + + div.appendChild(this.createTitle(mxResources.get('distribute'))); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.distributeCells(true); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.distributeCells(false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAngle = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + + var input = null; + var update = null; + var btn = null; + + if (ss.edges.length == 0) + { + mxUtils.write(span, mxResources.get('angle')); + div.appendChild(span); + + input = this.addUnitInput(div, '°', 20, 44, function() + { + update.apply(this, arguments); + }); + + mxUtils.br(div); + div.style.paddingTop = '10px'; + } + else + { + div.style.paddingTop = '8px'; + } + + if (!ss.containsLabel) + { + var label = mxResources.get('reverse'); + + if (ss.vertices.length > 0 && ss.edges.length > 0) + { + label = mxResources.get('turn') + ' / ' + label; + } + else if (ss.vertices.length > 0) + { + label = mxResources.get('turn'); + } + + btn = mxUtils.button(label, function(evt) + { + ui.actions.get('turn').funct(evt); + }) + + btn.setAttribute('title', label + ' (' + this.editorUi.actions.get('turn').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + if (input != null) + { + btn.style.marginTop = '8px'; + } + } + + if (input != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || document.activeElement != input) + { + ss = this.format.getSelectionState(); + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0)); + input.value = (isNaN(tmp)) ? '' : tmp + '°'; + } + }); + + update = this.installInputHandler(input, mxConstants.STYLE_ROTATION, 0, 0, 360, '°', null, true); + this.addKeyHandler(input, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + return div; +}; + +BaseFormatPanel.prototype.getUnit = function() +{ + var unit = this.editorUi.editor.graph.view.unit; + + switch(unit) + { + case mxConstants.POINTS: + return 'pt'; + case mxConstants.INCHES: + return '"'; + case mxConstants.MILLIMETERS: + return 'mm'; + } +}; + +BaseFormatPanel.prototype.inUnit = function(pixels) +{ + return this.editorUi.editor.graph.view.formatUnitText(pixels); +}; + +BaseFormatPanel.prototype.fromUnit = function(value) +{ + var unit = this.editorUi.editor.graph.view.unit; + + switch(unit) + { + case mxConstants.POINTS: + return value; + case mxConstants.INCHES: + return value * mxConstants.PIXELS_PER_INCH; + case mxConstants.MILLIMETERS: + return value * mxConstants.PIXELS_PER_MM; + } +}; + +BaseFormatPanel.prototype.isFloatUnit = function() +{ + return this.editorUi.editor.graph.view.unit != mxConstants.POINTS; +}; + +BaseFormatPanel.prototype.getUnitStep = function() +{ + var unit = this.editorUi.editor.graph.view.unit; + + switch(unit) + { + case mxConstants.POINTS: + return 1; + case mxConstants.INCHES: + return 0.1; + case mxConstants.MILLIMETERS: + return 0.5; + } +}; + +/** + * + */ +ArrangePanel.prototype.addGeometry = function(container) +{ + var panel = this; + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '50px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('size')); + div.appendChild(span); + + var widthUpdate, heightUpdate, leftUpdate, topUpdate; + var width = this.addUnitInput(div, this.getUnit(), 84, 44, function() + { + widthUpdate.apply(this, arguments); + }, this.getUnitStep(), null, null, this.isFloatUnit()); + var height = this.addUnitInput(div, this.getUnit(), 20, 44, function() + { + heightUpdate.apply(this, arguments); + }, this.getUnitStep(), null, null, this.isFloatUnit()); + + var autosizeBtn = document.createElement('div'); + autosizeBtn.className = 'geSprite geSprite-fit'; + autosizeBtn.setAttribute('title', mxResources.get('autosize') + ' (' + this.editorUi.actions.get('autosize').shortcut + ')'); + autosizeBtn.style.position = 'relative'; + autosizeBtn.style.cursor = 'pointer'; + autosizeBtn.style.marginTop = '-3px'; + autosizeBtn.style.border = '0px'; + autosizeBtn.style.left = '52px'; + mxUtils.setOpacity(autosizeBtn, 50); + + mxEvent.addListener(autosizeBtn, 'mouseenter', function() + { + mxUtils.setOpacity(autosizeBtn, 100); + }); + + mxEvent.addListener(autosizeBtn, 'mouseleave', function() + { + mxUtils.setOpacity(autosizeBtn, 50); + }); + + mxEvent.addListener(autosizeBtn, 'click', function() + { + ui.actions.get('autosize').funct(); + }); + + div.appendChild(autosizeBtn); + this.addLabel(div, mxResources.get('width'), 84); + this.addLabel(div, mxResources.get('height'), 20); + mxUtils.br(div); + + var wrapper = document.createElement('div'); + wrapper.style.paddingTop = '8px'; + wrapper.style.paddingRight = '20px'; + wrapper.style.whiteSpace = 'nowrap'; + wrapper.style.textAlign = 'right'; + var opt = this.createCellOption(mxResources.get('constrainProportions'), + mxConstants.STYLE_ASPECT, null, 'fixed', 'null'); + opt.style.width = '100%'; + wrapper.appendChild(opt); + div.appendChild(wrapper); + + var constrainCheckbox = opt.getElementsByTagName('input')[0]; + this.addKeyHandler(width, listener); + this.addKeyHandler(height, listener); + + widthUpdate = this.addGeometryHandler(width, function(geo, value) + { + if (geo.width > 0) + { + var value = Math.max(1, panel.fromUnit(value)); + + if (constrainCheckbox.checked) + { + geo.height = Math.round((geo.height * value * 100) / geo.width) / 100; + } + + geo.width = value; + } + }); + heightUpdate = this.addGeometryHandler(height, function(geo, value) + { + if (geo.height > 0) + { + var value = Math.max(1, panel.fromUnit(value)); + + if (constrainCheckbox.checked) + { + geo.width = Math.round((geo.width * value * 100) / geo.height) / 100; + } + + geo.height = value; + } + }); + + container.appendChild(div); + + var div2 = this.createPanel(); + div2.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('position')); + div2.appendChild(span); + + var left = this.addUnitInput(div2, this.getUnit(), 84, 44, function() + { + leftUpdate.apply(this, arguments); + }, this.getUnitStep(), null, null, this.isFloatUnit()); + var top = this.addUnitInput(div2, this.getUnit(), 20, 44, function() + { + topUpdate.apply(this, arguments); + }, this.getUnitStep(), null, null, this.isFloatUnit()); + + mxUtils.br(div2); + this.addLabel(div2, mxResources.get('left'), 84); + this.addLabel(div2, mxResources.get('top'), 20); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + + if (!rect.containsLabel && rect.vertices.length == graph.getSelectionCount() && + rect.width != null && rect.height != null) + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + width.value = this.inUnit(rect.width) + ((rect.width == '') ? '' : ' ' + this.getUnit()); + } + + if (force || document.activeElement != height) + { + height.value = this.inUnit(rect.height) + ((rect.height == '') ? '' : ' ' + this.getUnit()); + } + } + else + { + div.style.display = 'none'; + } + + if (rect.vertices.length == graph.getSelectionCount() && + rect.x != null && rect.y != null) + { + div2.style.display = ''; + + if (force || document.activeElement != left) + { + left.value = this.inUnit(rect.x) + ((rect.x == '') ? '' : ' ' + this.getUnit()); + } + + if (force || document.activeElement != top) + { + top.value = this.inUnit(rect.y) + ((rect.y == '') ? '' : ' ' + this.getUnit()); + } + } + else + { + div2.style.display = 'none'; + } + }); + + this.addKeyHandler(left, listener); + this.addKeyHandler(top, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + leftUpdate = this.addGeometryHandler(left, function(geo, value) + { + value = panel.fromUnit(value); + + if (geo.relative) + { + geo.offset.x = value; + } + else + { + geo.x = value; + } + }); + topUpdate = this.addGeometryHandler(top, function(geo, value) + { + value = panel.fromUnit(value); + + if (geo.relative) + { + geo.offset.y = value; + } + else + { + geo.y = value; + } + }); + + container.appendChild(div2); +}; + +/** + * + */ +ArrangePanel.prototype.addGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + var panel = this; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (isNaN(value)) + { + input.value = initialValue + ' ' + panel.getUnit(); + } + else if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + var state = graph.view.getState(cells[i]); + + if (state != null && graph.isRecursiveVertexResize(state)) + { + graph.resizeChildCells(cells[i], geo); + } + + graph.getModel().setGeometry(cells[i], geo); + graph.constrainChildCells(cells[i]); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' ' + panel.getUnit(); + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +ArrangePanel.prototype.addEdgeGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (isNaN(value)) + { + input.value = initialValue + ' pt'; + } + else if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isEdge(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' pt'; + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +/** + * + */ +ArrangePanel.prototype.addEdgeGeometry = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('width')); + div.appendChild(span); + + var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate; + var width = this.addUnitInput(div, 'pt', 20, 44, function() + { + widthUpdate.apply(this, arguments); + }); + + mxUtils.br(div); + this.addKeyHandler(width, listener); + + function widthUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(width.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(rect.style, 'width', mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth)) + { + graph.setCellStyles('width', value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['width'], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + width.value = value + ' pt'; + mxEvent.consume(evt); + }; + + mxEvent.addListener(width, 'blur', widthUpdate); + mxEvent.addListener(width, 'change', widthUpdate); + + container.appendChild(div); + + var divs = this.createPanel(); + divs.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'Start'); + divs.appendChild(span); + + var xs = this.addUnitInput(divs, 'pt', 84, 44, function() + { + xsUpdate.apply(this, arguments); + }); + var ys = this.addUnitInput(divs, 'pt', 20, 44, function() + { + ysUpdate.apply(this, arguments); + }); + + mxUtils.br(divs); + this.addLabel(divs, mxResources.get('left'), 84); + this.addLabel(divs, mxResources.get('top'), 20); + container.appendChild(divs); + this.addKeyHandler(xs, listener); + this.addKeyHandler(ys, listener); + + var divt = this.createPanel(); + divt.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'End'); + divt.appendChild(span); + + var xt = this.addUnitInput(divt, 'pt', 84, 44, function() + { + xtUpdate.apply(this, arguments); + }); + var yt = this.addUnitInput(divt, 'pt', 20, 44, function() + { + ytUpdate.apply(this, arguments); + }); + + mxUtils.br(divt); + this.addLabel(divt, mxResources.get('left'), 84); + this.addLabel(divt, mxResources.get('top'), 20); + container.appendChild(divt); + this.addKeyHandler(xt, listener); + this.addKeyHandler(yt, listener); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + var cell = graph.getSelectionCell(); + + if (rect.style.shape == 'link' || rect.style.shape == 'flexArrow') + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + var value = mxUtils.getValue(rect.style, 'width', + mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth); + width.value = value + ' pt'; + } + } + else + { + div.style.display = 'none'; + } + + if (graph.getSelectionCount() == 1 && graph.model.isEdge(cell)) + { + var geo = graph.model.getGeometry(cell); + + if (geo.sourcePoint != null && graph.model.getTerminal(cell, true) == null) + { + xs.value = geo.sourcePoint.x; + ys.value = geo.sourcePoint.y; + } + else + { + divs.style.display = 'none'; + } + + if (geo.targetPoint != null && graph.model.getTerminal(cell, false) == null) + { + xt.value = geo.targetPoint.x; + yt.value = geo.targetPoint.y; + } + else + { + divt.style.display = 'none'; + } + } + else + { + divs.style.display = 'none'; + divt.style.display = 'none'; + } + }); + + xsUpdate = this.addEdgeGeometryHandler(xs, function(geo, value) + { + geo.sourcePoint.x = value; + }); + + ysUpdate = this.addEdgeGeometryHandler(ys, function(geo, value) + { + geo.sourcePoint.y = value; + }); + + xtUpdate = this.addEdgeGeometryHandler(xt, function(geo, value) + { + geo.targetPoint.x = value; + }); + + ytUpdate = this.addEdgeGeometryHandler(yt, function(geo, value) + { + geo.targetPoint.y = value; + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(TextFormatPanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.init = function() +{ + this.container.style.borderBottom = 'none'; + this.addFont(this.container); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.addFont = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var title = this.createTitle(mxResources.get('font')); + title.style.paddingLeft = '18px'; + title.style.paddingTop = '10px'; + title.style.paddingBottom = '6px'; + container.appendChild(title); + + var stylePanel = this.createPanel(); + stylePanel.style.paddingTop = '2px'; + stylePanel.style.paddingBottom = '2px'; + stylePanel.style.position = 'relative'; + stylePanel.style.marginLeft = '-2px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + stylePanel.style.display = 'block'; + } + + if (graph.cellEditor.isContentEditing()) + { + var cssPanel = stylePanel.cloneNode(); + + var cssMenu = this.editorUi.toolbar.addMenu(mxResources.get('style'), + mxResources.get('style'), true, 'formatBlock', cssPanel, null, true); + cssMenu.style.color = 'rgb(112, 112, 112)'; + cssMenu.style.whiteSpace = 'nowrap'; + cssMenu.style.overflow = 'hidden'; + cssMenu.style.margin = '0px'; + this.addArrow(cssMenu); + cssMenu.style.width = '192px'; + cssMenu.style.height = '15px'; + + var arrow = cssMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + container.appendChild(cssPanel); + } + + container.appendChild(stylePanel); + + var colorPanel = this.createPanel(); + colorPanel.style.marginTop = '8px'; + colorPanel.style.borderTop = '1px solid #c0c0c0'; + colorPanel.style.paddingTop = '6px'; + colorPanel.style.paddingBottom = '6px'; + + var fontMenu = this.editorUi.toolbar.addMenu('Helvetica', mxResources.get('fontFamily'), + true, 'fontFamily', stylePanel, null, true); + fontMenu.style.color = 'rgb(112, 112, 112)'; + fontMenu.style.whiteSpace = 'nowrap'; + fontMenu.style.overflow = 'hidden'; + fontMenu.style.margin = '0px'; + + this.addArrow(fontMenu); + fontMenu.style.width = '192px'; + fontMenu.style.height = '15px'; + + var stylePanel2 = stylePanel.cloneNode(false); + stylePanel2.style.marginLeft = '-3px'; + var fontStyleItems = this.editorUi.toolbar.addItems(['bold', 'italic', 'underline'], stylePanel2, true); + fontStyleItems[0].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); + fontStyleItems[1].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); + fontStyleItems[2].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); + + var verticalItem = this.editorUi.toolbar.addItems(['vertical'], stylePanel2, true)[0]; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel2); + + this.styleButtons(fontStyleItems); + this.styleButtons([verticalItem]); + + var stylePanel3 = stylePanel.cloneNode(false); + stylePanel3.style.marginLeft = '-3px'; + stylePanel3.style.paddingBottom = '0px'; + + // Helper function to return a wrapper function does not pass any arguments + var callFn = function(fn) + { + return function() + { + return fn(); + }; + }; + + var left = this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + (graph.cellEditor.isContentEditing()) ? + function(evt) + { + graph.cellEditor.alignText(mxConstants.ALIGN_LEFT, evt); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT])), stylePanel3); + var center = this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + (graph.cellEditor.isContentEditing()) ? + function(evt) + { + graph.cellEditor.alignText(mxConstants.ALIGN_CENTER, evt); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER])), stylePanel3); + var right = this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + (graph.cellEditor.isContentEditing()) ? + function(evt) + { + graph.cellEditor.alignText(mxConstants.ALIGN_RIGHT, evt); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT])), stylePanel3); + + this.styleButtons([left, center, right]); + + // Quick hack for strikethrough + // TODO: Add translations and toggle state + if (graph.cellEditor.isContentEditing()) + { + var strike = this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('strikethrough'), + function() + { + document.execCommand('strikeThrough', false, null); + }, stylePanel2); + this.styleButtons([strike]); + + strike.firstChild.style.background = 'url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjR2MjRIMFYweiIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9ImIiPjx1c2UgeGxpbms6aHJlZj0iI2EiIG92ZXJmbG93PSJ2aXNpYmxlIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjYikiIGZpbGw9IiMwMTAxMDEiIGQ9Ik03LjI0IDguNzVjLS4yNi0uNDgtLjM5LTEuMDMtLjM5LTEuNjcgMC0uNjEuMTMtMS4xNi40LTEuNjcuMjYtLjUuNjMtLjkzIDEuMTEtMS4yOS40OC0uMzUgMS4wNS0uNjMgMS43LS44My42Ni0uMTkgMS4zOS0uMjkgMi4xOC0uMjkuODEgMCAxLjU0LjExIDIuMjEuMzQuNjYuMjIgMS4yMy41NCAxLjY5Ljk0LjQ3LjQuODMuODggMS4wOCAxLjQzLjI1LjU1LjM4IDEuMTUuMzggMS44MWgtMy4wMWMwLS4zMS0uMDUtLjU5LS4xNS0uODUtLjA5LS4yNy0uMjQtLjQ5LS40NC0uNjgtLjItLjE5LS40NS0uMzMtLjc1LS40NC0uMy0uMS0uNjYtLjE2LTEuMDYtLjE2LS4zOSAwLS43NC4wNC0xLjAzLjEzLS4yOS4wOS0uNTMuMjEtLjcyLjM2LS4xOS4xNi0uMzQuMzQtLjQ0LjU1LS4xLjIxLS4xNS40My0uMTUuNjYgMCAuNDguMjUuODguNzQgMS4yMS4zOC4yNS43Ny40OCAxLjQxLjdINy4zOWMtLjA1LS4wOC0uMTEtLjE3LS4xNS0uMjV6TTIxIDEydi0ySDN2Mmg5LjYyYy4xOC4wNy40LjE0LjU1LjIuMzcuMTcuNjYuMzQuODcuNTEuMjEuMTcuMzUuMzYuNDMuNTcuMDcuMi4xMS40My4xMS42OSAwIC4yMy0uMDUuNDUtLjE0LjY2LS4wOS4yLS4yMy4zOC0uNDIuNTMtLjE5LjE1LS40Mi4yNi0uNzEuMzUtLjI5LjA4LS42My4xMy0xLjAxLjEzLS40MyAwLS44My0uMDQtMS4xOC0uMTNzLS42Ni0uMjMtLjkxLS40MmMtLjI1LS4xOS0uNDUtLjQ0LS41OS0uNzUtLjE0LS4zMS0uMjUtLjc2LS4yNS0xLjIxSDYuNGMwIC41NS4wOCAxLjEzLjI0IDEuNTguMTYuNDUuMzcuODUuNjUgMS4yMS4yOC4zNS42LjY2Ljk4LjkyLjM3LjI2Ljc4LjQ4IDEuMjIuNjUuNDQuMTcuOS4zIDEuMzguMzkuNDguMDguOTYuMTMgMS40NC4xMy44IDAgMS41My0uMDkgMi4xOC0uMjhzMS4yMS0uNDUgMS42Ny0uNzljLjQ2LS4zNC44Mi0uNzcgMS4wNy0xLjI3cy4zOC0xLjA3LjM4LTEuNzFjMC0uNi0uMS0xLjE0LS4zMS0xLjYxLS4wNS0uMTEtLjExLS4yMy0uMTctLjMzSDIxeiIvPjwvc3ZnPg==)'; + strike.firstChild.style.backgroundPosition = '2px 2px'; + strike.firstChild.style.backgroundSize = '18px 18px'; + + this.styleButtons([strike]); + } + + var top = this.editorUi.toolbar.addButton('geSprite-top', mxResources.get('top'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP])), stylePanel3); + var middle = this.editorUi.toolbar.addButton('geSprite-middle', mxResources.get('middle'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE])), stylePanel3); + var bottom = this.editorUi.toolbar.addButton('geSprite-bottom', mxResources.get('bottom'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM])), stylePanel3); + + this.styleButtons([top, middle, bottom]); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel3); + + // Hack for updating UI state below based on current text selection + // currentTable is the current selected DOM table updated below + var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow; + + if (graph.cellEditor.isContentEditing()) + { + top.style.display = 'none'; + middle.style.display = 'none'; + bottom.style.display = 'none'; + verticalItem.style.display = 'none'; + + full = this.editorUi.toolbar.addButton('geSprite-justifyfull', mxResources.get('block'), + function() + { + if (full.style.opacity == 1) + { + document.execCommand('justifyfull', false, null); + } + }, stylePanel3); + full.style.marginRight = '9px'; + full.style.opacity = 1; + + this.styleButtons([full, + sub = this.editorUi.toolbar.addButton('geSprite-subscript', + mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)', + function() + { + document.execCommand('subscript', false, null); + }, stylePanel3), sup = this.editorUi.toolbar.addButton('geSprite-superscript', + mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)', + function() + { + document.execCommand('superscript', false, null); + }, stylePanel3)]); + sub.style.marginLeft = '9px'; + + var tmp = stylePanel3.cloneNode(false); + tmp.style.paddingTop = '4px'; + var btns = [this.editorUi.toolbar.addButton('geSprite-orderedlist', mxResources.get('numberedList'), + function() + { + document.execCommand('insertorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-unorderedlist', mxResources.get('bulletedList'), + function() + { + document.execCommand('insertunorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-outdent', mxResources.get('decreaseIndent'), + function() + { + document.execCommand('outdent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-indent', mxResources.get('increaseIndent'), + function() + { + document.execCommand('indent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('removeFormat'), + function() + { + document.execCommand('removeformat', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-code', mxResources.get('html'), + function() + { + graph.cellEditor.toggleViewMode(); + }, tmp)]; + this.styleButtons(btns); + btns[btns.length - 2].style.marginLeft = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + tmp.style.height = '40'; + } + + container.appendChild(tmp); + } + else + { + fontStyleItems[2].style.marginRight = '9px'; + right.style.marginRight = '9px'; + } + + // Label position + var stylePanel4 = stylePanel.cloneNode(false); + stylePanel4.style.marginLeft = '0px'; + stylePanel4.style.paddingTop = '8px'; + stylePanel4.style.paddingBottom = '4px'; + stylePanel4.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel4, mxResources.get('position')); + + // Adds label position options + var positionSelect = document.createElement('select'); + positionSelect.style.position = 'absolute'; + positionSelect.style.right = '20px'; + positionSelect.style.width = '97px'; + positionSelect.style.marginTop = '-2px'; + + var directions = ['topLeft', 'top', 'topRight', 'left', 'center', 'right', 'bottomLeft', 'bottom', 'bottomRight']; + var lset = {'topLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM], + 'top': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM], + 'topRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM], + 'left': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE], + 'center': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE], + 'right': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE], + 'bottomLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP], + 'bottom': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP], + 'bottomRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP]}; + + for (var i = 0; i < directions.length; i++) + { + var positionOption = document.createElement('option'); + positionOption.setAttribute('value', directions[i]); + mxUtils.write(positionOption, mxResources.get(directions[i])); + positionSelect.appendChild(positionOption); + } + + stylePanel4.appendChild(positionSelect); + + // Writing direction + var stylePanel5 = stylePanel.cloneNode(false); + stylePanel5.style.marginLeft = '0px'; + stylePanel5.style.paddingTop = '4px'; + stylePanel5.style.paddingBottom = '4px'; + stylePanel5.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel5, mxResources.get('writingDirection')); + + // Adds writing direction options + // LATER: Handle reselect of same option in all selects (change event + // is not fired for same option so have opened state on click) and + // handle multiple different styles for current selection + var dirSelect = document.createElement('select'); + dirSelect.style.position = 'absolute'; + dirSelect.style.right = '20px'; + dirSelect.style.width = '97px'; + dirSelect.style.marginTop = '-2px'; + + // NOTE: For automatic we use the value null since automatic + // requires the text to be non formatted and non-wrapped + var dirs = ['automatic', 'leftToRight', 'rightToLeft']; + var dirSet = {'automatic': null, + 'leftToRight': mxConstants.TEXT_DIRECTION_LTR, + 'rightToLeft': mxConstants.TEXT_DIRECTION_RTL}; + + for (var i = 0; i < dirs.length; i++) + { + var dirOption = document.createElement('option'); + dirOption.setAttribute('value', dirs[i]); + mxUtils.write(dirOption, mxResources.get(dirs[i])); + dirSelect.appendChild(dirOption); + } + + stylePanel5.appendChild(dirSelect); + + if (!graph.isEditing()) + { + container.appendChild(stylePanel4); + + mxEvent.addListener(positionSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var vals = lset[positionSelect.value]; + + if (vals != null) + { + graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, vals[0], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, vals[1], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, vals[3], graph.getSelectionCells()); + } + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // LATER: Update dir in text editor while editing and update style with label + // NOTE: The tricky part is handling and passing on the auto value + container.appendChild(stylePanel5); + + mxEvent.addListener(dirSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_TEXT_DIRECTION, dirSet[dirSelect.value], graph.getSelectionCells()); + mxEvent.consume(evt); + }); + } + + // Font size + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + } + + input.style.width = '46px'; + input.style.height = (mxClient.IS_QUIRKS) ? '21px' : '17px'; + stylePanel2.appendChild(input); + + // Workaround for font size 4 if no text is selected is update font size below + // after first character was entered (as the font element is lazy created) + var pendingFontSize = null; + + var inputUpdate = this.installInputHandler(input, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize, 1, 999, ' pt', + function(fontSize) + { + // IE does not support containsNode + // KNOWN: Fixes font size issues but bypasses undo + if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + var container = (selection.rangeCount > 0) ? selection.getRangeAt(0).commonAncestorContainer : + graph.cellEditor.textarea; + + function updateSize(elt, ignoreContains) + { + if (graph.cellEditor.textarea != null && elt != graph.cellEditor.textarea && + graph.cellEditor.textarea.contains(elt) && + (ignoreContains || selection.containsNode(elt, true))) + { + if (elt.nodeName == 'FONT') + { + elt.removeAttribute('size'); + elt.style.fontSize = fontSize + 'px'; + } + else + { + var css = mxUtils.getCurrentStyle(elt); + + if (css.fontSize != fontSize + 'px') + { + if (mxUtils.getCurrentStyle(elt.parentNode).fontSize != fontSize + 'px') + { + elt.style.fontSize = fontSize + 'px'; + } + else + { + elt.style.fontSize = ''; + } + } + } + } + }; + + // Wraps text node or mixed selection with leading text in a font element + if (container == graph.cellEditor.textarea || + container.nodeType != mxConstants.NODETYPE_ELEMENT) + { + document.execCommand('fontSize', false, '1'); + } + + if (container != graph.cellEditor.textarea) + { + container = container.parentNode; + } + + if (container != null && container.nodeType == mxConstants.NODETYPE_ELEMENT) + { + var elts = container.getElementsByTagName('*'); + updateSize(container); + + for (var i = 0; i < elts.length; i++) + { + updateSize(elts[i]); + } + } + + input.value = fontSize + ' pt'; + } + else if (window.getSelection || document.selection) + { + // Checks selection + var par = null; + + if (document.selection) + { + par = document.selection.createRange().parentElement(); + } + else + { + var selection = window.getSelection(); + + if (selection.rangeCount > 0) + { + par = selection.getRangeAt(0).commonAncestorContainer; + } + } + + // Node.contains does not work for text nodes in IE11 + function isOrContains(container, node) + { + while (node != null) + { + if (node === container) + { + return true; + } + + node = node.parentNode; + } + + return false; + }; + + if (par != null && isOrContains(graph.cellEditor.textarea, par)) + { + pendingFontSize = fontSize; + + // Workaround for can't set font size in px is to change font size afterwards + document.execCommand('fontSize', false, '4'); + var elts = graph.cellEditor.textarea.getElementsByTagName('font'); + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].getAttribute('size') == '4') + { + elts[i].removeAttribute('size'); + elts[i].style.fontSize = pendingFontSize + 'px'; + + // Overrides fontSize in input with the one just assigned as a workaround + // for potential fontSize values of parent elements that don't match + window.setTimeout(function() + { + input.value = pendingFontSize + ' pt'; + pendingFontSize = null; + }, 0); + + break; + } + } + } + } + }, true); + + var stepper = this.createStepper(input, inputUpdate, 1, 10, true, Menus.prototype.defaultFontSize); + stepper.style.display = input.style.display; + stepper.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + stepper.style.right = '20px'; + } + + stylePanel2.appendChild(stepper); + + var arrow = fontMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + + var bgColorApply = null; + var currentBgColor = '#ffffff'; + + var fontColorApply = null; + var currentFontColor = '#000000'; + + var bgPanel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('backgroundColor'), function() + { + return currentBgColor; + }, function(color) + { + document.execCommand('backcolor', false, (color != mxConstants.NONE) ? color : 'transparent'); + }, '#ffffff', + { + install: function(apply) { bgColorApply = apply; }, + destroy: function() { bgColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('backgroundColor'), mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, '#ffffff', null, function(color) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.backgroundColor = null; + }); + }); + bgPanel.style.fontWeight = 'bold'; + + var borderPanel = this.createCellColorOption(mxResources.get('borderColor'), mxConstants.STYLE_LABEL_BORDERCOLOR, '#000000'); + borderPanel.style.fontWeight = 'bold'; + + var panel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('fontColor'), function() + { + return currentFontColor; + }, function(color) + { + if (mxClient.IS_FF) + { + // Workaround for Firefox that adds the font element around + // anchor elements which ignore inherited colors is to move + // the font element inside anchor elements + var tmp = graph.cellEditor.textarea.getElementsByTagName('font'); + var oldFonts = []; + + for (var i = 0; i < tmp.length; i++) + { + oldFonts.push( + { + node: tmp[i], + color: tmp[i].getAttribute('color') + }); + } + + document.execCommand('forecolor', false, (color != mxConstants.NONE) ? + color : 'transparent'); + + // Finds the new or changed font element + var newFonts = graph.cellEditor.textarea.getElementsByTagName('font'); + + for (var i = 0; i < newFonts.length; i++) + { + if (i >= oldFonts.length || newFonts[i] != oldFonts[i].node || + (newFonts[i] == oldFonts[i].node && + newFonts[i].getAttribute('color') != oldFonts[i].color)) + { + var child = newFonts[i].firstChild; + + // Moves the font element to inside the anchor element and adopts all children + if (child != null && child.nodeName == 'A' && child.nextSibling == + null && + child.firstChild != null) + { + var parent = newFonts[i].parentNode; + parent.insertBefore(child, newFonts[i]); + var tmp = child.firstChild; + + while (tmp != null) + { + var next = tmp.nextSibling; + newFonts[i].appendChild(tmp); + tmp = next; + } + + child.appendChild(newFonts[i]); + } + + break; + } + } + } + else + { + document.execCommand('forecolor', false, (color != mxConstants.NONE) ? + color : 'transparent'); + } + }, '#000000', + { + install: function(apply) { fontColorApply = apply; }, + destroy: function() { fontColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('fontColor'), mxConstants.STYLE_FONTCOLOR, '#000000', function(color) + { + if (color == null || color == mxConstants.NONE) + { + bgPanel.style.display = 'none'; + } + else + { + bgPanel.style.display = ''; + } + + borderPanel.style.display = bgPanel.style.display; + }, function(color) + { + if (color == null || color == mxConstants.NONE) + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, '1', graph.getSelectionCells()); + } + else + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, graph.getSelectionCells()); + } + + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.removeAttribute('color'); + elt.style.color = null; + }); + }); + panel.style.fontWeight = 'bold'; + + colorPanel.appendChild(panel); + colorPanel.appendChild(bgPanel); + + if (!graph.cellEditor.isContentEditing()) + { + colorPanel.appendChild(borderPanel); + } + + container.appendChild(colorPanel); + + var extraPanel = this.createPanel(); + extraPanel.style.paddingTop = '2px'; + extraPanel.style.paddingBottom = '4px'; + + // LATER: Fix toggle using '' instead of 'null' + var wwOpt = this.createCellOption(mxResources.get('wordWrap'), mxConstants.STYLE_WHITE_SPACE, null, 'wrap', 'null', null, null, true); + wwOpt.style.fontWeight = 'bold'; + + // Word wrap in edge labels only supported via labelWidth style + if (!ss.containsLabel && !ss.autoSize && ss.edges.length == 0) + { + extraPanel.appendChild(wwOpt); + } + + // Delegates switch of style to formattedText action as it also convertes newlines + var htmlOpt = this.createCellOption(mxResources.get('formattedText'), 'html', '0', + null, null, null, ui.actions.get('formattedText')); + htmlOpt.style.fontWeight = 'bold'; + extraPanel.appendChild(htmlOpt); + + var spacingPanel = this.createPanel(); + spacingPanel.style.paddingTop = '10px'; + spacingPanel.style.paddingBottom = '28px'; + spacingPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('spacing')); + spacingPanel.appendChild(span); + + var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate; + var topSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + topUpdate.apply(this, arguments); + }); + var globalSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + globalUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('top'), 91); + this.addLabel(spacingPanel, mxResources.get('global'), 20); + mxUtils.br(spacingPanel); + mxUtils.br(spacingPanel); + + var leftSpacing = this.addUnitInput(spacingPanel, 'pt', 162, 44, function() + { + leftUpdate.apply(this, arguments); + }); + var bottomSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + bottomUpdate.apply(this, arguments); + }); + var rightSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + rightUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('left'), 162); + this.addLabel(spacingPanel, mxResources.get('bottom'), 91); + this.addLabel(spacingPanel, mxResources.get('right'), 20); + + if (!graph.cellEditor.isContentEditing()) + { + container.appendChild(extraPanel); + container.appendChild(this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_TEXT_OPACITY)); + container.appendChild(spacingPanel); + } + else + { + var selState = null; + var lineHeightInput = null; + + container.appendChild(this.createRelativeOption(mxResources.get('lineheight'), null, null, function(input) + { + var value = (input.value == '') ? 120 : parseInt(input.value); + value = Math.max(0, (isNaN(value)) ? 120 : value); + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null && node == graph.cellEditor.textarea && graph.cellEditor.textarea.firstChild != null) + { + if (graph.cellEditor.textarea.firstChild.nodeName != 'P') + { + graph.cellEditor.textarea.innerHTML = '

' + graph.cellEditor.textarea.innerHTML + '

'; + } + + node = graph.cellEditor.textarea.firstChild; + } + + if (node != null && graph.cellEditor.textarea != null && node != graph.cellEditor.textarea && + graph.cellEditor.textarea.contains(node)) + { + node.style.lineHeight = value + '%'; + } + + input.value = value + ' %'; + }, function(input) + { + // Used in CSS handler to update current value + lineHeightInput = input; + + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + input.value = '120 %'; + })); + + var insertPanel = stylePanel.cloneNode(false); + insertPanel.style.paddingLeft = '0px'; + var insertBtns = this.editorUi.toolbar.addItems(['link', 'image'], insertPanel, true); + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-horizontalrule', mxResources.get('insertHorizontalRule'), + function() + { + document.execCommand('inserthorizontalrule', false); + }, insertPanel), + this.editorUi.toolbar.addMenuFunctionInContainer(insertPanel, 'geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.addInsertTableItem(menu); + }))]; + this.styleButtons(insertBtns); + this.styleButtons(btns); + + var wrapper2 = this.createPanel(); + wrapper2.style.paddingTop = '10px'; + wrapper2.style.paddingBottom = '10px'; + wrapper2.appendChild(this.createTitle(mxResources.get('insert'))); + wrapper2.appendChild(insertPanel); + container.appendChild(wrapper2); + + if (mxClient.IS_QUIRKS) + { + wrapper2.style.height = '70'; + } + + var tablePanel = stylePanel.cloneNode(false); + tablePanel.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex : 0); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex + 1 : -1); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableCell != null) + { + graph.deleteColumn(currentTable, tableCell.cellIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.insertRow(currentTable, tableRow.sectionRowIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.insertRow(currentTable, tableRow.sectionRowIndex + 1); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.deleteRow(currentTable, tableRow.sectionRowIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + var wrapper3 = this.createPanel(); + wrapper3.style.paddingTop = '10px'; + wrapper3.style.paddingBottom = '10px'; + wrapper3.appendChild(this.createTitle(mxResources.get('table'))); + wrapper3.appendChild(tablePanel); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + wrapper3.style.height = '70'; + } + + var tablePanel2 = stylePanel.cloneNode(false); + tablePanel2.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-strokecolor', mxResources.get('borderColor'), + mxUtils.bind(this, function(evt) + { + if (currentTable != null) + { + // Converts rgb(r,g,b) values + var color = currentTable.style.borderColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + var targetElt = (tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))) ? tableCell : currentTable; + + graph.processElements(targetElt, function(elt) + { + elt.style.border = null; + }); + + if (newColor == null || newColor == mxConstants.NONE) + { + targetElt.removeAttribute('border'); + targetElt.style.border = ''; + targetElt.style.borderCollapse = ''; + } + else + { + targetElt.setAttribute('border', '1'); + targetElt.style.border = '1px solid ' + newColor; + targetElt.style.borderCollapse = 'collapse'; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fillcolor', mxResources.get('backgroundColor'), + mxUtils.bind(this, function(evt) + { + // Converts rgb(r,g,b) values + if (currentTable != null) + { + var color = currentTable.style.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + var targetElt = (tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))) ? tableCell : currentTable; + + graph.processElements(targetElt, function(elt) + { + elt.style.backgroundColor = null; + }); + + if (newColor == null || newColor == mxConstants.NONE) + { + targetElt.style.backgroundColor = ''; + } + else + { + targetElt.style.backgroundColor = newColor; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fit', mxResources.get('spacing'), + function() + { + if (currentTable != null) + { + var value = currentTable.getAttribute('cellPadding') || 0; + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + if (newValue != null && newValue.length > 0) + { + currentTable.setAttribute('cellPadding', newValue); + } + else + { + currentTable.removeAttribute('cellPadding'); + } + }), mxResources.get('spacing')); + ui.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'left'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'center'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'right'); + } + }, tablePanel2)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(wrapper3); + mxUtils.br(wrapper3); + } + + wrapper3.appendChild(tablePanel2); + container.appendChild(wrapper3); + + tableWrapper = wrapper3; + } + + function setSelected(elt, selected) + { + if (mxClient.IS_IE && (mxClient.IS_QUIRKS || document.documentMode < 10)) + { + elt.style.filter = (selected) ? 'progid:DXImageTransform.Microsoft.Gradient('+ + 'StartColorStr=\'#c5ecff\', EndColorStr=\'#87d4fb\', GradientType=0)' : ''; + } + else + { + elt.style.backgroundImage = (selected) ? 'linear-gradient(#c5ecff 0px,#87d4fb 100%)' : ''; + } + }; + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0); + setSelected(fontStyleItems[0], (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD); + setSelected(fontStyleItems[1], (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC); + setSelected(fontStyleItems[2], (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE); + fontMenu.firstChild.nodeValue = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTFAMILY, Menus.prototype.defaultFont); + + setSelected(verticalItem, mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, '1') == '0'); + + if (force || document.activeElement != input) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + var align = mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); + setSelected(left, align == mxConstants.ALIGN_LEFT); + setSelected(center, align == mxConstants.ALIGN_CENTER); + setSelected(right, align == mxConstants.ALIGN_RIGHT); + + var valign = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); + setSelected(top, valign == mxConstants.ALIGN_TOP); + setSelected(middle, valign == mxConstants.ALIGN_MIDDLE); + setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM); + + var pos = mxUtils.getValue(ss.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + var vpos = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE); + + if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'top'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topRight'; + } + else if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottom'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomRight'; + } + else if (pos == mxConstants.ALIGN_LEFT) + { + positionSelect.value = 'left'; + } + else if (pos == mxConstants.ALIGN_RIGHT) + { + positionSelect.value = 'right'; + } + else + { + positionSelect.value = 'center'; + } + + var dir = mxUtils.getValue(ss.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION); + + if (dir == mxConstants.TEXT_DIRECTION_RTL) + { + dirSelect.value = 'rightToLeft'; + } + else if (dir == mxConstants.TEXT_DIRECTION_LTR) + { + dirSelect.value = 'leftToRight'; + } + else if (dir == mxConstants.TEXT_DIRECTION_AUTO) + { + dirSelect.value = 'automatic'; + } + + if (force || document.activeElement != globalSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2)); + globalSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != topSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0)); + topSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != rightSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0)); + rightSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != bottomSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0)); + bottomSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != leftSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0)); + leftSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + globalUpdate = this.installInputHandler(globalSpacing, mxConstants.STYLE_SPACING, 2, -999, 999, ' pt'); + topUpdate = this.installInputHandler(topSpacing, mxConstants.STYLE_SPACING_TOP, 0, -999, 999, ' pt'); + rightUpdate = this.installInputHandler(rightSpacing, mxConstants.STYLE_SPACING_RIGHT, 0, -999, 999, ' pt'); + bottomUpdate = this.installInputHandler(bottomSpacing, mxConstants.STYLE_SPACING_BOTTOM, 0, -999, 999, ' pt'); + leftUpdate = this.installInputHandler(leftSpacing, mxConstants.STYLE_SPACING_LEFT, 0, -999, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(globalSpacing, listener); + this.addKeyHandler(topSpacing, listener); + this.addKeyHandler(rightSpacing, listener); + this.addKeyHandler(bottomSpacing, listener); + this.addKeyHandler(leftSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + if (graph.cellEditor.isContentEditing()) + { + var updating = false; + + var updateCssHandler = function() + { + if (!updating) + { + updating = true; + + window.setTimeout(function() + { + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null) + { + // Workaround for commonAncestor on range in IE11 returning parent of common ancestor + if (node == graph.cellEditor.textarea && graph.cellEditor.textarea.children.length == 1 && + graph.cellEditor.textarea.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) + { + node = graph.cellEditor.textarea.firstChild; + } + + function getRelativeLineHeight(fontSize, css, elt) + { + if (elt.style != null && css != null) + { + var lineHeight = css.lineHeight + + if (elt.style.lineHeight != null && elt.style.lineHeight.substring(elt.style.lineHeight.length - 1) == '%') + { + return parseInt(elt.style.lineHeight) / 100; + } + else + { + return (lineHeight.substring(lineHeight.length - 2) == 'px') ? + parseFloat(lineHeight) / fontSize : parseInt(lineHeight); + } + } + else + { + return ''; + } + }; + + function getAbsoluteFontSize(css) + { + var fontSize = (css != null) ? css.fontSize : null; + + if (fontSize != null && fontSize.substring(fontSize.length - 2) == 'px') + { + return parseFloat(fontSize); + } + else + { + return mxConstants.DEFAULT_FONTSIZE; + } + }; + + var css = mxUtils.getCurrentStyle(node); + var fontSize = getAbsoluteFontSize(css); + var lineHeight = getRelativeLineHeight(fontSize, css, node); + + // Finds common font size + var elts = node.getElementsByTagName('*'); + + // IE does not support containsNode + if (elts.length > 0 && window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + + for (var i = 0; i < elts.length; i++) + { + if (selection.containsNode(elts[i], true)) + { + temp = mxUtils.getCurrentStyle(elts[i]); + fontSize = Math.max(getAbsoluteFontSize(temp), fontSize); + var lh = getRelativeLineHeight(fontSize, temp, elts[i]); + + if (lh != lineHeight || isNaN(lh)) + { + lineHeight = ''; + } + } + } + } + + function hasParentOrOnlyChild(name) + { + if (graph.getParentByName(node, name, graph.cellEditor.textarea) != null) + { + return true; + } + else + { + var child = node; + + while (child != null && child.childNodes.length == 1) + { + child = child.childNodes[0]; + + if (child.nodeName == name) + { + return true; + } + } + } + + return false; + }; + + function isEqualOrPrefixed(str, value) + { + if (str != null && value != null) + { + if (str == value) + { + return true; + } + else if (str.length > value.length + 1) + { + return str.substring(str.length - value.length - 1, str.length) == '-' + value; + } + } + + return false; + }; + + if (css != null) + { + setSelected(fontStyleItems[0], css.fontWeight == 'bold' || + css.fontWeight > 400 || hasParentOrOnlyChild('B') || + hasParentOrOnlyChild('STRONG')); + setSelected(fontStyleItems[1], css.fontStyle == 'italic' || + hasParentOrOnlyChild('I') || hasParentOrOnlyChild('EM')); + setSelected(fontStyleItems[2], hasParentOrOnlyChild('U')); + setSelected(sup, hasParentOrOnlyChild('SUP')); + setSelected(sub, hasParentOrOnlyChild('SUB')); + + if (!graph.cellEditor.isTableSelected()) + { + var align = graph.cellEditor.align || mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); + + if (isEqualOrPrefixed(css.textAlign, 'justify')) + { + setSelected(full, isEqualOrPrefixed(css.textAlign, 'justify')); + setSelected(left, false); + setSelected(center, false); + setSelected(right, false); + } + else + { + setSelected(full, false); + setSelected(left, align == mxConstants.ALIGN_LEFT); + setSelected(center, align == mxConstants.ALIGN_CENTER); + setSelected(right, align == mxConstants.ALIGN_RIGHT); + } + } + else + { + setSelected(full, isEqualOrPrefixed(css.textAlign, 'justify')); + setSelected(left, isEqualOrPrefixed(css.textAlign, 'left')); + setSelected(center, isEqualOrPrefixed(css.textAlign, 'center')); + setSelected(right, isEqualOrPrefixed(css.textAlign, 'right')); + } + + currentTable = graph.getParentByName(node, 'TABLE', graph.cellEditor.textarea); + tableRow = (currentTable == null) ? null : graph.getParentByName(node, 'TR', currentTable); + tableCell = (currentTable == null) ? null : graph.getParentByNames(node, ['TD', 'TH'], currentTable); + tableWrapper.style.display = (currentTable != null) ? '' : 'none'; + + if (document.activeElement != input) + { + if (node.nodeName == 'FONT' && node.getAttribute('size') == '4' && + pendingFontSize != null) + { + node.removeAttribute('size'); + node.style.fontSize = pendingFontSize + ' pt'; + pendingFontSize = null; + } + else + { + input.value = (isNaN(fontSize)) ? '' : fontSize + ' pt'; + } + + var lh = parseFloat(lineHeight); + + if (!isNaN(lh)) + { + lineHeightInput.value = Math.round(lh * 100) + ' %'; + } + else + { + lineHeightInput.value = '100 %'; + } + } + + // Converts rgb(r,g,b) values + var color = css.color.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + var color2 = css.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + + // Updates the color picker for the current font + if (fontColorApply != null) + { + if (color.charAt(0) == '#') + { + currentFontColor = color; + } + else + { + currentFontColor = '#000000'; + } + + fontColorApply(currentFontColor, true); + } + + if (bgColorApply != null) + { + if (color2.charAt(0) == '#') + { + currentBgColor = color2; + } + else + { + currentBgColor = null; + } + + bgColorApply(currentBgColor, true); + } + + // Workaround for firstChild is null or not an object + // in the log which seems to be IE8- only / 29.01.15 + if (fontMenu.firstChild != null) + { + // Strips leading and trailing quotes + var ff = css.fontFamily; + + if (ff.charAt(0) == '\'') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '\'') + { + ff = ff.substring(0, ff.length - 1); + } + + if (ff.charAt(0) == '"') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '"') + { + ff = ff.substring(0, ff.length - 1); + } + + fontMenu.firstChild.nodeValue = ff; + } + } + } + + updating = false; + }, 0); + } + }; + + if (mxClient.IS_FF || mxClient.IS_EDGE || mxClient.IS_IE || mxClient.IS_IE11) + { + mxEvent.addListener(graph.cellEditor.textarea, 'DOMSubtreeModified', updateCssHandler); + } + + mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); + this.listeners.push({destroy: function() + { + // No need to remove listener since textarea is destroyed after edit + }}); + updateCssHandler(); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(StyleFormatPanel, BaseFormatPanel); + +/** + * + */ +StyleFormatPanel.prototype.defaultStrokeColor = 'black'; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + if (!ss.containsLabel) + { + if (ss.containsImage && ss.vertices.length == 1 && ss.style.shape == 'image' && + ss.style.image != null && ss.style.image.substring(0, 19) == 'data:image/svg+xml;') + { + this.container.appendChild(this.addSvgStyles(this.createPanel())); + } + + if (!ss.containsImage || ss.style.shape == 'image') + { + this.container.appendChild(this.addFill(this.createPanel())); + } + + this.container.appendChild(this.addStroke(this.createPanel())); + this.container.appendChild(this.addLineJumps(this.createPanel())); + var opacityPanel = this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_OPACITY, 41); + opacityPanel.style.paddingTop = '8px'; + opacityPanel.style.paddingBottom = '8px'; + this.container.appendChild(opacityPanel); + this.container.appendChild(this.addEffects(this.createPanel())); + } + + var opsPanel = this.addEditOps(this.createPanel()); + + if (opsPanel.firstChild != null) + { + mxUtils.br(opsPanel); + } + + this.container.appendChild(this.addStyleOps(opsPanel)); +}; + +/** + * Use browser for parsing CSS. + */ +StyleFormatPanel.prototype.getCssRules = function(css) +{ + var doc = document.implementation.createHTMLDocument(''); + var styleElement = document.createElement('style'); + + mxUtils.setTextContent(styleElement, css); + doc.body.appendChild(styleElement); + + return styleElement.sheet.cssRules; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgStyles = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + container.style.fontWeight = 'bold'; + container.style.display = 'none'; + + try + { + var exp = ss.style.editableCssRules; + + if (exp != null) + { + var regex = new RegExp(exp); + + var data = ss.style.image.substring(ss.style.image.indexOf(',') + 1); + var xml = (window.atob) ? atob(data) : Base64.decode(data, true); + var svg = mxUtils.parseXml(xml); + + if (svg != null) + { + var styles = svg.getElementsByTagName('style'); + + for (var i = 0; i < styles.length; i++) + { + var rules = this.getCssRules(mxUtils.getTextContent(styles[i])); + + for (var j = 0; j < rules.length; j++) + { + this.addSvgRule(container, rules[j], svg, styles[i], rules, j, regex); + } + } + } + } + } + catch (e) + { + // ignore + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgRule = function(container, rule, svg, styleElem, rules, ruleIndex, regex) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + + if (regex.test(rule.selectorText)) + { + function rgb2hex(rgb) + { + rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + + return (rgb && rgb.length === 4) ? "#" + + ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + }; + + var addStyleRule = mxUtils.bind(this, function(rule, key, label) + { + if (rule.style[key] != '') + { + var option = this.createColorOption(label + ' ' + rule.selectorText, function() + { + return rgb2hex(rule.style[key]); + }, function(color) + { + rules[ruleIndex].style[key] = color; + var cssTxt = ''; + + for (var i = 0; i < rules.length; i++) + { + cssTxt += rules[i].cssText + ' '; + } + + styleElem.textContent = cssTxt; + var xml = mxUtils.getXml(svg.documentElement); + + graph.setCellStyles(mxConstants.STYLE_IMAGE, 'data:image/svg+xml,' + + ((window.btoa) ? btoa(xml) : Base64.encode(xml, true)), + graph.getSelectionCells()); + }, '#ffffff', + { + install: function(apply) + { + // ignore + }, + destroy: function() + { + // ignore + } + }); + + container.appendChild(option); + + // Shows container if rules are added + container.style.display = ''; + } + }); + + addStyleRule(rule, 'fill', mxResources.get('fill')); + addStyleRule(rule, 'stroke', mxResources.get('line')); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEditOps = function(div) +{ + var ss = this.format.getSelectionState(); + var btn = null; + + if (this.editorUi.editor.graph.getSelectionCount() == 1) + { + btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + } + + if (ss.image) + { + var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('image').funct(); + })); + + btn2.setAttribute('title', mxResources.get('editImage')); + btn2.style.marginBottom = '2px'; + + if (btn == null) + { + btn2.style.width = '202px'; + } + else + { + btn.style.width = '100px'; + btn2.style.width = '100px'; + btn2.style.marginLeft = '2px'; + } + + div.appendChild(btn2); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addFill = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + + // Adds gradient direction option + var gradientSelect = document.createElement('select'); + gradientSelect.style.position = 'absolute'; + gradientSelect.style.marginTop = '-2px'; + gradientSelect.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + gradientSelect.style.width = '70px'; + + // Stops events from bubbling to color option event handler + mxEvent.addListener(gradientSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var gradientPanel = this.createCellColorOption(mxResources.get('gradient'), mxConstants.STYLE_GRADIENTCOLOR, '#ffffff', function(color) + { + if (color == null || color == mxConstants.NONE) + { + gradientSelect.style.display = 'none'; + } + else + { + gradientSelect.style.display = ''; + } + }); + + var fillKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BACKGROUND : mxConstants.STYLE_FILLCOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('background') : mxResources.get('fill'); + + var fillPanel = this.createCellColorOption(label, fillKey, '#ffffff'); + fillPanel.style.fontWeight = 'bold'; + + var tmpColor = mxUtils.getValue(ss.style, fillKey, null); + gradientPanel.style.display = (tmpColor != null && tmpColor != mxConstants.NONE && + ss.fill && ss.style.shape != 'image') ? '' : 'none'; + + var directions = [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST, + mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST]; + + for (var i = 0; i < directions.length; i++) + { + var gradientOption = document.createElement('option'); + gradientOption.setAttribute('value', directions[i]); + mxUtils.write(gradientOption, mxResources.get(directions[i])); + gradientSelect.appendChild(gradientOption); + } + + gradientPanel.appendChild(gradientSelect); + + var listener = mxUtils.bind(this, function() + { + ss = this.format.getSelectionState(); + var value = mxUtils.getValue(ss.style, mxConstants.STYLE_GRADIENT_DIRECTION, mxConstants.DIRECTION_SOUTH); + + // Handles empty string which is not allowed as a value + if (value == '') + { + value = mxConstants.DIRECTION_SOUTH; + } + + gradientSelect.value = value; + container.style.display = (ss.fill) ? '' : 'none'; + + var fillColor = mxUtils.getValue(ss.style, mxConstants.STYLE_FILLCOLOR, null); + + if (!ss.fill || ss.containsImage || fillColor == null || fillColor == mxConstants.NONE || ss.style.shape == 'filledEdge') + { + gradientPanel.style.display = 'none'; + } + else + { + gradientPanel.style.display = ''; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + mxEvent.addListener(gradientSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION, gradientSelect.value, graph.getSelectionCells()); + mxEvent.consume(evt); + }); + + container.appendChild(fillPanel); + container.appendChild(gradientPanel); + + // Adds custom colors + var custom = this.getCustomColors(); + + for (var i = 0; i < custom.length; i++) + { + container.appendChild(this.createCellColorOption(custom[i].title, custom[i].key, custom[i].defaultValue)); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.getCustomColors = function() +{ + var ss = this.format.getSelectionState(); + var result = []; + + if (ss.style.shape == 'swimlane') + { + result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); + } + + return result; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStroke = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + + container.style.paddingTop = '4px'; + container.style.paddingBottom = '4px'; + container.style.whiteSpace = 'normal'; + + var colorPanel = document.createElement('div'); + colorPanel.style.fontWeight = 'bold'; + + // Adds gradient direction option + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '72px'; + styleSelect.style.width = '80px'; + + var styles = ['sharp', 'rounded', 'curved']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED]; + // Default for rounded is 1 + var values = ['0', null]; + + if (styleSelect.value == 'rounded') + { + values = ['1', null]; + } + else if (styleSelect.value == 'curved') + { + values = [null, '1']; + } + + for (var i = 0; i < keys.length; i++) + { + graph.setCellStyles(keys[i], values[i], graph.getSelectionCells()); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', keys, + 'values', values, 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var strokeKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BORDER : mxConstants.STYLE_STROKECOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('border') : mxResources.get('line'); + + var lineColor = this.createCellColorOption(label, strokeKey, '#000000'); + lineColor.appendChild(styleSelect); + colorPanel.appendChild(lineColor); + + // Used if only edges selected + var stylePanel = colorPanel.cloneNode(false); + stylePanel.style.fontWeight = 'normal'; + stylePanel.style.whiteSpace = 'nowrap'; + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '16px' + stylePanel.style.marginBottom = '2px'; + stylePanel.style.marginTop = '2px'; + stylePanel.className = 'geToolbarContainer'; + + var addItem = mxUtils.bind(this, function(menu, width, cssName, keys, values) + { + var item = this.editorUi.menus.styleChange(menu, '', keys, values, 'geIcon', null); + + var pat = document.createElement('div'); + pat.style.width = width + 'px'; + pat.style.height = '1px'; + pat.style.borderBottom = '1px ' + cssName + ' ' + this.defaultStrokeColor; + pat.style.paddingTop = '6px'; + + item.firstChild.firstChild.style.padding = '0px 4px 0px 4px'; + item.firstChild.firstChild.style.width = width + 'px'; + item.firstChild.firstChild.appendChild(pat); + + return item; + }); + + var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 75, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 75, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + // Used for mixed selection (vertices and edges) + var altStylePanel = stylePanel.cloneNode(false); + + var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-connection', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], [null, null, null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); + })); + + var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 33, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 33, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + var stylePanel2 = stylePanel.cloneNode(false); + + // Stroke width + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '2px'; + input.style.width = '41px'; + input.setAttribute('title', mxResources.get('linewidth')); + + stylePanel.appendChild(input); + + var altInput = input.cloneNode(true); + altStylePanel.appendChild(altInput); + + function update(evt) + { + // Maximum stroke width is 999 + var value = parseInt(input.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = value + ' pt'; + mxEvent.consume(evt); + }; + + function altUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(altInput.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + altInput.value = value + ' pt'; + mxEvent.consume(evt); + }; + + var stepper = this.createStepper(input, update, 1, 9); + stepper.style.display = input.style.display; + stepper.style.marginTop = '2px'; + stylePanel.appendChild(stepper); + + var altStepper = this.createStepper(altInput, altUpdate, 1, 9); + altStepper.style.display = altInput.style.display; + altStepper.style.marginTop = '2px'; + altStylePanel.appendChild(altStepper); + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + input.style.height = '15px'; + stepper.style.right = '20px'; + + altInput.style.position = 'absolute'; + altInput.style.right = '32px'; + altInput.style.height = '15px'; + altStepper.style.right = '20px'; + } + else + { + input.style.height = '17px'; + altInput.style.height = '17px'; + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + mxEvent.addListener(altInput, 'blur', altUpdate); + mxEvent.addListener(altInput, 'change', altUpdate); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(stylePanel2); + mxUtils.br(stylePanel2); + } + + var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape != 'arrow') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + + if (ss.style.shape == 'connector') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); + } + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); + } + })); + + var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-startclassic', mxResources.get('linestart'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-startclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-startclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-startopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-startopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['openAsync', 0], 'geIcon geSprite geSprite-startopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-startblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-startblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 1], 'geIcon geSprite geSprite-startasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-startoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-startdiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-startthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-startclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-startclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-startblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 0], 'geIcon geSprite geSprite-startasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-startovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-startdiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-startthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['box', 0], 'geIcon geSprite geSvgSprite geSprite-box', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['halfCircle', 0], 'geIcon geSprite geSvgSprite geSprite-halfCircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['dash', 0], 'geIcon geSprite geSprite-startdash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['cross', 0], 'geIcon geSprite geSprite-startcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-startcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circle', 1], 'geIcon geSprite geSprite-startcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERone', 0], 'geIcon geSprite geSprite-starterone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-starteronetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmany', 0], 'geIcon geSprite geSprite-startermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-starteronetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-starteroneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-startermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-endclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-endclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-endopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-endopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['openAsync', 0], 'geIcon geSprite geSprite-endopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-endblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-endblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 1], 'geIcon geSprite geSprite-endasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-endoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-enddiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-endthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-endclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-endclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-endblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 0], 'geIcon geSprite geSprite-endasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-endovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-enddiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-endthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['box', 0], 'geIcon geSprite geSvgSprite geFlipSprite geSprite-box', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['halfCircle', 0], 'geIcon geSprite geSvgSprite geFlipSprite geSprite-halfCircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['dash', 0], 'geIcon geSprite geSprite-enddash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['cross', 0], 'geIcon geSprite geSprite-endcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-endcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circle', 1], 'geIcon geSprite geSprite-endcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERone', 0], 'geIcon geSprite geSprite-enderone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-enderonetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmany', 0], 'geIcon geSprite geSprite-endermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-enderonetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-enderoneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-endermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + this.addArrow(edgeShape, 8); + this.addArrow(edgeStyle); + this.addArrow(lineStart); + this.addArrow(lineEnd); + + var symbol = this.addArrow(pattern, 9); + symbol.className = 'geIcon'; + symbol.style.width = '84px'; + + var altSymbol = this.addArrow(altPattern, 9); + altSymbol.className = 'geIcon'; + altSymbol.style.width = '22px'; + + var solid = document.createElement('div'); + solid.style.width = '85px'; + solid.style.height = '1px'; + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + solid.style.marginBottom = '9px'; + symbol.appendChild(solid); + + var altSolid = document.createElement('div'); + altSolid.style.width = '23px'; + altSolid.style.height = '1px'; + altSolid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + altSolid.style.marginBottom = '9px'; + altSymbol.appendChild(altSolid); + + pattern.style.height = '15px'; + altPattern.style.height = '15px'; + edgeShape.style.height = '15px'; + edgeStyle.style.height = '17px'; + lineStart.style.marginLeft = '3px'; + lineStart.style.height = '17px'; + lineEnd.style.marginLeft = '3px'; + lineEnd.style.height = '17px'; + + container.appendChild(colorPanel); + container.appendChild(altStylePanel); + container.appendChild(stylePanel); + + var arrowPanel = stylePanel.cloneNode(false); + arrowPanel.style.paddingBottom = '6px'; + arrowPanel.style.paddingTop = '4px'; + arrowPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '2px'; + span.style.fontWeight = 'normal'; + span.style.width = '76px'; + + mxUtils.write(span, mxResources.get('lineend')); + arrowPanel.appendChild(span); + + var endSpacingUpdate, endSizeUpdate; + var endSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + endSpacingUpdate.apply(this, arguments); + }); + var endSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + endSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + + var spacer = document.createElement('div'); + spacer.style.height = '8px'; + arrowPanel.appendChild(spacer); + + span = span.cloneNode(false); + mxUtils.write(span, mxResources.get('linestart')); + arrowPanel.appendChild(span); + + var startSpacingUpdate, startSizeUpdate; + var startSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + startSpacingUpdate.apply(this, arguments); + }); + var startSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + startSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + this.addLabel(arrowPanel, mxResources.get('spacing'), 74, 50); + this.addLabel(arrowPanel, mxResources.get('size'), 20, 50); + mxUtils.br(arrowPanel); + + var perimeterPanel = colorPanel.cloneNode(false); + perimeterPanel.style.fontWeight = 'normal'; + perimeterPanel.style.position = 'relative'; + perimeterPanel.style.paddingLeft = '16px' + perimeterPanel.style.marginBottom = '2px'; + perimeterPanel.style.marginTop = '6px'; + perimeterPanel.style.borderWidth = '0px'; + perimeterPanel.style.paddingBottom = '18px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '1px'; + span.style.fontWeight = 'normal'; + span.style.width = '120px'; + mxUtils.write(span, mxResources.get('perimeter')); + perimeterPanel.appendChild(span); + + var perimeterUpdate; + var perimeterSpacing = this.addUnitInput(perimeterPanel, 'pt', 20, 41, function() + { + perimeterUpdate.apply(this, arguments); + }); + + if (ss.edges.length == graph.getSelectionCount()) + { + container.appendChild(stylePanel2); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + mxUtils.br(container); + } + + container.appendChild(arrowPanel); + } + else if (ss.vertices.length == graph.getSelectionCount()) + { + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(perimeterPanel); + } + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var color = mxUtils.getValue(ss.style, strokeKey, null); + + if (force || document.activeElement != input) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != altInput) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + altInput.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + styleSelect.style.visibility = (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') ? '' : 'hidden'; + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + styleSelect.value = 'curved'; + } + else if (mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == '1') + { + styleSelect.value = 'rounded'; + } + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == '1') + { + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null) + { + solid.style.borderBottom = '1px dashed ' + this.defaultStrokeColor; + } + else + { + solid.style.borderBottom = '1px dotted ' + this.defaultStrokeColor; + } + } + else + { + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + } + + altSolid.style.borderBottom = solid.style.borderBottom; + + // Updates toolbar icon for edge style + var edgeStyleDiv = edgeStyle.getElementsByTagName('div')[0]; + var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null); + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == '1') + { + es = null; + } + + if (es == 'orthogonalEdgeStyle' && mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + edgeStyleDiv.className = 'geSprite geSprite-curved'; + } + else if (es == 'straight' || es == 'none' || es == null) + { + edgeStyleDiv.className = 'geSprite geSprite-straight'; + } + else if (es == 'entityRelationEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-entity'; + } + else if (es == 'elbowEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalelbow' : 'geSprite-horizontalelbow'); + } + else if (es == 'isometricEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalisometric' : 'geSprite-horizontalisometric'); + } + else + { + edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; + } + + // Updates icon for edge shape + var edgeShapeDiv = edgeShape.getElementsByTagName('div')[0]; + + if (ss.style.shape == 'link') + { + edgeShapeDiv.className = 'geSprite geSprite-linkedge'; + } + else if (ss.style.shape == 'flexArrow') + { + edgeShapeDiv.className = 'geSprite geSprite-arrow'; + } + else if (ss.style.shape == 'arrow') + { + edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; + } + else + { + edgeShapeDiv.className = 'geSprite geSprite-connection'; + } + + if (ss.edges.length == graph.getSelectionCount()) + { + altStylePanel.style.display = ''; + stylePanel.style.display = 'none'; + } + else + { + altStylePanel.style.display = 'none'; + stylePanel.style.display = ''; + } + + function updateArrow(marker, fill, elt, prefix) + { + var markerDiv = elt.getElementsByTagName('div')[0]; + + markerDiv.className = ui.getCssClassForMarker(prefix, ss.style.shape, marker, fill); + + if (markerDiv.className == 'geSprite geSprite-noarrow') + { + markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('none')); + markerDiv.style.backgroundImage = 'none'; + markerDiv.style.verticalAlign = 'top'; + markerDiv.style.marginTop = '5px'; + markerDiv.style.fontSize = '10px'; + markerDiv.style.filter = 'none'; + markerDiv.style.color = this.defaultStrokeColor; + markerDiv.nextSibling.style.marginTop = '0px'; + } + + return markerDiv; + }; + + var sourceDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null), + mxUtils.getValue(ss.style, 'startFill', '1'), lineStart, 'start'); + var targetDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null), + mxUtils.getValue(ss.style, 'endFill', '1'), lineEnd, 'end'); + + // Special cases for markers + if (ss.style.shape == 'arrow') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-endblocktrans'; + } + else if (ss.style.shape == 'link') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-noarrow'; + } + + mxUtils.setOpacity(edgeStyle, (ss.style.shape == 'arrow') ? 30 : 100); + + if (ss.style.shape != 'connector' && ss.style.shape != 'flexArrow' && ss.style.shape != 'filledEdge') + { + mxUtils.setOpacity(lineStart, 30); + mxUtils.setOpacity(lineEnd, 30); + } + else + { + mxUtils.setOpacity(lineStart, 100); + mxUtils.setOpacity(lineEnd, 100); + } + + if (force || document.activeElement != startSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE)); + startSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0)); + startSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != endSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)); + endSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0)); + endSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != perimeterSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0)); + perimeterSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + startSizeUpdate = this.installInputHandler(startSize, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + startSpacingUpdate = this.installInputHandler(startSpacing, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0, -999, 999, ' pt'); + endSizeUpdate = this.installInputHandler(endSize, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + endSpacingUpdate = this.installInputHandler(endSpacing, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0, -999, 999, ' pt'); + perimeterUpdate = this.installInputHandler(perimeterSpacing, mxConstants.STYLE_PERIMETER_SPACING, 0, 0, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(startSize, listener); + this.addKeyHandler(startSpacing, listener); + this.addKeyHandler(endSize, listener); + this.addKeyHandler(endSpacing, listener); + this.addKeyHandler(perimeterSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return container; +}; + +/** + * Adds UI for configuring line jumps. + */ +StyleFormatPanel.prototype.addLineJumps = function(container) +{ + var ss = this.format.getSelectionState(); + + if (Graph.lineJumpsEnabled && ss.edges.length > 0 && + ss.vertices.length == 0 && ss.lineJumps) + { + container.style.padding = '8px 0px 24px 18px'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.fontWeight = 'bold'; + span.style.width = '80px'; + + mxUtils.write(span, mxResources.get('lineJumps')); + container.appendChild(span); + + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '76px'; + styleSelect.style.width = '62px'; + + var styles = ['none', 'arc', 'gap', 'sharp']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles('jumpStyle', styleSelect.value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['jumpStyle'], + 'values', [styleSelect.value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + container.appendChild(styleSelect); + + var jumpSizeUpdate; + + var jumpSize = this.addUnitInput(container, 'pt', 22, 33, function() + { + jumpSizeUpdate.apply(this, arguments); + }); + + jumpSizeUpdate = this.installInputHandler(jumpSize, 'jumpSize', + Graph.defaultJumpSize, 0, 999, ' pt'); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + styleSelect.value = mxUtils.getValue(ss.style, 'jumpStyle', 'none'); + + if (force || document.activeElement != jumpSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, 'jumpSize', Graph.defaultJumpSize)); + jumpSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + this.addKeyHandler(jumpSize, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + else + { + container.style.display = 'none'; + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEffects = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingTop = '0px'; + div.style.paddingBottom = '2px'; + + var table = document.createElement('table'); + + if (mxClient.IS_QUIRKS) + { + table.style.fontSize = '1em'; + } + + table.style.width = '100%'; + table.style.fontWeight = 'bold'; + table.style.paddingRight = '20px'; + var tbody = document.createElement('tbody'); + var row = document.createElement('tr'); + row.style.padding = '0px'; + var left = document.createElement('td'); + left.style.padding = '0px'; + left.style.width = '50%'; + left.setAttribute('valign', 'top'); + + var right = left.cloneNode(true); + right.style.paddingLeft = '8px'; + row.appendChild(left); + row.appendChild(right); + tbody.appendChild(row); + table.appendChild(tbody); + div.appendChild(table); + + var current = left; + var count = 0; + + var addOption = mxUtils.bind(this, function(label, key, defaultValue) + { + var opt = this.createCellOption(label, key, defaultValue); + opt.style.width = '100%'; + current.appendChild(opt); + current = (current == left) ? right : left; + count++; + }); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + + left.innerHTML = ''; + right.innerHTML = ''; + current = left; + + if (ss.rounded) + { + addOption(mxResources.get('rounded'), mxConstants.STYLE_ROUNDED, 0); + } + + if (ss.style.shape == 'swimlane') + { + addOption(mxResources.get('divider'), 'swimlaneLine', 1); + } + + if (!ss.containsImage) + { + addOption(mxResources.get('shadow'), mxConstants.STYLE_SHADOW, 0); + } + + if (ss.glass) + { + addOption(mxResources.get('glass'), mxConstants.STYLE_GLASS, 0); + } + + if (ss.comic) + { + addOption(mxResources.get('comic'), 'comic', 0); + } + + if (count == 0) + { + div.style.display = 'none'; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return div; +} + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStyleOps = function(div) +{ + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + + var btn = mxUtils.button(mxResources.get('setAsDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('setAsDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('setAsDefaultStyle') + ' (' + this.editorUi.actions.get('setAsDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(DiagramFormatPanel, BaseFormatPanel); + +/** + * Switch to disable page view. + */ +DiagramFormatPanel.showPageView = true; + +/** + * Specifies if the background image option should be shown. Default is true. + */ +DiagramFormatPanel.prototype.showBackgroundImageOption = true; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + //this.container.appendChild(this.addView(this.createPanel())); + + if (graph.isEnabled()) + { + //this.container.appendChild(this.addOptions(this.createPanel())); + //this.container.appendChild(this.addPaperSize(this.createPanel())); + //this.container.appendChild(this.addStyleOps(this.createPanel())); + + //Make each error break line when out of screen + var logsDIV = this.createPanel() + logsDIV.style.whiteSpace = 'pre-line'; + this.container.appendChild(this.addErrorLogs(logsDIV)); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addView = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('view'))); + + // Grid + this.addGridOption(div); + + // Page View + if (DiagramFormatPanel.showPageView) + { + div.appendChild(this.createOption(mxResources.get('pageView'), function() + { + return graph.pageVisible; + }, function(checked) + { + ui.actions.get('pageView').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.pageVisible); + }; + + ui.addListener('pageViewChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + if (graph.isEnabled()) + { + // Background + var bg = this.createColorOption(mxResources.get('background'), function() + { + return graph.background; + }, function(color) + { + var change = new ChangePageSetup(ui, color); + change.ignoreImage = true; + + graph.model.execute(change); + }, '#ffffff', + { + install: function(apply) + { + this.listener = function() + { + apply(graph.background); + }; + + ui.addListener('backgroundColorChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + if (this.showBackgroundImageOption) + { + var btn = mxUtils.button(mxResources.get('image'), function(evt) + { + ui.showBackgroundImageDialog(); + mxEvent.consume(evt); + }) + + btn.style.position = 'absolute'; + btn.className = 'geColorBtn'; + btn.style.marginTop = '-4px'; + btn.style.paddingBottom = (document.documentMode == 11 || mxClient.IS_MT) ? '0px' : '2px'; + btn.style.height = '22px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + btn.style.width = '56px'; + + bg.appendChild(btn); + } + + div.appendChild(bg); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addOptions = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + if (graph.isEnabled()) + { + // Connection arrows + div.appendChild(this.createOption(mxResources.get('connectionArrows'), function() + { + return graph.connectionArrowsEnabled; + }, function(checked) + { + ui.actions.get('connectionArrows').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionArrowsEnabled); + }; + + ui.addListener('connectionArrowsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Connection points + div.appendChild(this.createOption(mxResources.get('connectionPoints'), function() + { + return graph.connectionHandler.isEnabled(); + }, function(checked) + { + ui.actions.get('connectionPoints').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionHandler.isEnabled()); + }; + + ui.addListener('connectionPointsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Guides + div.appendChild(this.createOption(mxResources.get('guides'), function() + { + return graph.graphHandler.guidesEnabled; + }, function(checked) + { + ui.actions.get('guides').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.graphHandler.guidesEnabled); + }; + + ui.addListener('guidesEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + return div; +}; + +/** + * + */ +DiagramFormatPanel.prototype.addGridOption = function(container) +{ + var fPanel = this; + var ui = this.editorUi; + var graph = ui.editor.graph; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.width = '38px'; + input.value = this.inUnit(graph.getGridSize()) + ' ' + this.getUnit(); + + var stepper = this.createStepper(input, update, this.getUnitStep(), null, null, null, this.isFloatUnit()); + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + input.value = graph.getGridSize(); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + function update(evt) + { + var value = fPanel.isFloatUnit()? parseFloat(input.value) : parseInt(input.value); + value = fPanel.fromUnit(Math.max(fPanel.inUnit(1), (isNaN(value)) ? fPanel.inUnit(10) : value)); + + if (value != graph.getGridSize()) + { + graph.setGridSize(value) + } + + input.value = fPanel.inUnit(value) + ' ' + fPanel.getUnit(); + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + var unitChangeListener = function(sender, evt) + { + input.value = fPanel.inUnit(graph.getGridSize()) + ' ' + fPanel.getUnit(); + fPanel.format.refresh(); + }; + + graph.view.addListener('unitChanged', unitChangeListener); + this.listeners.push({destroy: function() { graph.view.removeListener(unitChangeListener); }}); + + if (mxClient.IS_SVG) + { + input.style.marginTop = '-2px'; + input.style.right = '84px'; + stepper.style.marginTop = '-16px'; + stepper.style.right = '72px'; + + var panel = this.createColorOption(mxResources.get('grid'), function() + { + var color = graph.view.gridColor; + + return (graph.isGridEnabled()) ? color : null; + }, function(color) + { + var enabled = graph.isGridEnabled(); + + if (color == mxConstants.NONE) + { + graph.setGridEnabled(false); + } + else + { + graph.setGridEnabled(true); + ui.setGridColor(color); + } + + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + if (enabled != graph.isGridEnabled()) + { + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + } + }, '#e0e0e0', + { + install: function(apply) + { + this.listener = function() + { + apply((graph.isGridEnabled()) ? graph.view.gridColor : null); + }; + + ui.addListener('gridColorChanged', this.listener); + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + panel.appendChild(input); + panel.appendChild(stepper); + container.appendChild(panel); + } + else + { + input.style.marginTop = '2px'; + input.style.right = '32px'; + stepper.style.marginTop = '2px'; + stepper.style.right = '20px'; + + container.appendChild(input); + container.appendChild(stepper); + + container.appendChild(this.createOption(mxResources.get('grid'), function() + { + return graph.isGridEnabled(); + }, function(checked) + { + graph.setGridEnabled(checked); + + if (graph.isGridEnabled()) + { + graph.view.gridColor = '#e0e0e0'; + } + + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, + { + install: function(apply) + { + this.listener = function() + { + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + apply(graph.isGridEnabled()); + }; + + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addDocumentProperties = function(div) +{ + // Hook for subclassers + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + return div; +}; + +DiagramFormatPanel.prototype.addErrorLogs = function(div) +{ + + div.appendChild(this.createTitle("Error Logs")); + div.style.width = "" + // mxUtils.writeln(div, "A very long long a incredible descriptive text in this small window example"); + // mxUtils.writeln(div, "Tex example2"); + + // $("#errorlogs_list > p").remove() + + this.editorUi.UPDATE_LOGS(div) + // if (this.editorUi.editor.errorlogs_array) { + // this.editorUi.editor.errorlogs_array + // .forEach(msg => { + // mxUtils.writeln(div, msg); + // // $("#errorlogs_list").append('

' + msg + '

') + // }) + // } + + div.id = "errorlogs_list" + + return div; +} + + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addPaperSize = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('paperSize'))); + + var accessor = PageSetupDialog.addPageFormatPanel(div, 'formatpanel', graph.pageFormat, function(pageFormat) + { + if (graph.pageFormat == null || graph.pageFormat.width != pageFormat.width || + graph.pageFormat.height != pageFormat.height) + { + var change = new ChangePageSetup(ui, null, null, pageFormat); + change.ignoreColor = true; + change.ignoreImage = true; + + graph.model.execute(change); + } + }); + + this.addKeyHandler(accessor.widthInput, function() + { + accessor.set(graph.pageFormat); + }); + this.addKeyHandler(accessor.heightInput, function() + { + accessor.set(graph.pageFormat); + }); + + var listener = function() + { + accessor.set(graph.pageFormat); + }; + + ui.addListener('pageFormatChanged', listener); + this.listeners.push({destroy: function() { ui.removeListener(listener); }}); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addStyleOps = function(div) +{ + var btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + mxUtils.br(div); + + btn = mxUtils.button(mxResources.get('clearDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearDefaultStyle') + ' (' + this.editorUi.actions.get('clearDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.destroy = function() +{ + BaseFormatPanel.prototype.destroy.apply(this, arguments); + + if (this.gridEnabledListener) + { + this.editorUi.removeListener(this.gridEnabledListener); + this.gridEnabledListener = null; + } +}; diff --git a/static/mxgraph/examples/grapheditor/www/js/Graph.js b/static/mxgraph/examples/grapheditor/www/js/Graph.js new file mode 100644 index 0000000..a9de2c4 --- /dev/null +++ b/static/mxgraph/examples/grapheditor/www/js/Graph.js @@ -0,0 +1,10486 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +// Workaround for allowing target="_blank" in HTML sanitizer +// see https://code.google.com/p/google-caja/issues/detail?can=2&q=&colspec=ID%20Type%20Status%20Priority%20Owner%20Summary&groupby=&sort=&id=1296 +if (typeof html4 !== 'undefined') +{ + html4.ATTRIBS["a::target"] = 0; + html4.ATTRIBS["source::src"] = 0; + html4.ATTRIBS["video::src"] = 0; + // Would be nice for tooltips but probably a security risk... + //html4.ATTRIBS["video::autoplay"] = 0; + //html4.ATTRIBS["video::autobuffer"] = 0; +} + +// Workaround for handling named HTML entities in mxUtils.parseXml +// LATER: How to configure DOMParser to just ignore all entities? +(function() +{ + var entities = [ + ['nbsp', '160'], + ['shy', '173'] + ]; + + var parseXml = mxUtils.parseXml; + + mxUtils.parseXml = function(text) + { + for (var i = 0; i < entities.length; i++) + { + text = text.replace(new RegExp( + '&' + entities[i][0] + ';', 'g'), + '&#' + entities[i][1] + ';'); + } + + return parseXml(text); + }; +})(); + +// Shim for missing toISOString in older versions of IE +// See https://stackoverflow.com/questions/12907862 +if (!Date.prototype.toISOString) +{ + (function() + { + function pad(number) + { + var r = String(number); + + if (r.length === 1) + { + r = '0' + r; + } + + return r; + }; + + Date.prototype.toISOString = function() + { + return this.getUTCFullYear() + + '-' + pad( this.getUTCMonth() + 1 ) + + '-' + pad( this.getUTCDate() ) + + 'T' + pad( this.getUTCHours() ) + + ':' + pad( this.getUTCMinutes() ) + + ':' + pad( this.getUTCSeconds() ) + + '.' + String( (this.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 ) + + 'Z'; + }; + }()); +} + +// Shim for Date.now() +if (!Date.now) +{ + Date.now = function() + { + return new Date().getTime(); + }; +} + +// Changes default colors +/** + * Measurements Units + */ +mxConstants.POINTS = 1; +mxConstants.MILLIMETERS = 2; +mxConstants.INCHES = 3; +/** + * This ratio is with page scale 1 + */ +mxConstants.PIXELS_PER_MM = 3.937; +mxConstants.PIXELS_PER_INCH = 100; + +mxConstants.SHADOW_OPACITY = 0.25; +mxConstants.SHADOWCOLOR = '#000000'; +mxConstants.VML_SHADOWCOLOR = '#d0d0d0'; +mxGraph.prototype.pageBreakColor = '#c0c0c0'; +mxGraph.prototype.pageScale = 1; + +// Letter page format is default in US, Canada and Mexico +(function() +{ + try + { + if (navigator != null && navigator.language != null) + { + var lang = navigator.language.toLowerCase(); + mxGraph.prototype.pageFormat = (lang === 'en-us' || lang === 'en-ca' || lang === 'es-mx') ? + mxConstants.PAGE_FORMAT_LETTER_PORTRAIT : mxConstants.PAGE_FORMAT_A4_PORTRAIT; + } + } + catch (e) + { + // ignore + } +})(); + +// Matches label positions of mxGraph 1.x +mxText.prototype.baseSpacingTop = 5; +mxText.prototype.baseSpacingBottom = 1; + +// Keeps edges between relative child cells inside parent +mxGraphModel.prototype.ignoreRelativeEdgeParent = false; + +// Defines grid properties +mxGraphView.prototype.gridImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCgAKAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAAKAAoAAAIJ1I6py+0Po2wFADs=' : + IMAGE_PATH + '/grid.gif'; +mxGraphView.prototype.gridSteps = 4; +mxGraphView.prototype.minGridSize = 4; + +// UrlParams is null in embed mode +mxGraphView.prototype.defaultGridColor = '#d0d0d0'; +mxGraphView.prototype.gridColor = mxGraphView.prototype.defaultGridColor; + +//Units +mxGraphView.prototype.unit = mxConstants.POINTS; + +mxGraphView.prototype.setUnit = function(unit) +{ + if (this.unit != unit) + { + this.unit = unit; + + this.fireEvent(new mxEventObject('unitChanged', 'unit', unit)); + } +}; + +// Alternative text for unsupported foreignObjects +mxSvgCanvas2D.prototype.foAltText = '[Not supported by viewer]'; + +// Hook for custom constraints +mxShape.prototype.getConstraints = function(style, w, h) +{ + return null; +}; + +/** + * Constructs a new graph instance. Note that the constructor does not take a + * container because the graph instance is needed for creating the UI, which + * in turn will create the container for the graph. Hence, the container is + * assigned later in EditorUi. + */ +/** + * Defines graph class. + */ +Graph = function(container, model, renderHint, stylesheet, themes, standalone) +{ + mxGraph.call(this, container, model, renderHint, stylesheet); + + this.themes = themes || this.defaultThemes; + this.currentEdgeStyle = mxUtils.clone(this.defaultEdgeStyle); + this.currentVertexStyle = mxUtils.clone(this.defaultVertexStyle); + this.standalone = (standalone != null) ? standalone : false; + + // Sets the base domain URL and domain path URL for relative links. + var b = this.baseUrl; + var p = b.indexOf('//'); + this.domainUrl = ''; + this.domainPathUrl = ''; + + if (p > 0) + { + var d = b.indexOf('/', p + 2); + + if (d > 0) + { + this.domainUrl = b.substring(0, d); + } + + d = b.lastIndexOf('/'); + + if (d > 0) + { + this.domainPathUrl = b.substring(0, d + 1); + } + } + + // Adds support for HTML labels via style. Note: Currently, only the Java + // backend supports HTML labels but CSS support is limited to the following: + // http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html + // TODO: Wrap should not affect isHtmlLabel output (should be handled later) + this.isHtmlLabel = function(cell) + { + var style = this.getCurrentCellStyle(cell); + + return (style != null) ? (style['html'] == '1' || style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') : false; + }; + + // Implements a listener for hover and click handling on edges + if (this.edgeMode) + { + var start = { + point: null, + event: null, + state: null, + handle: null, + selected: false + }; + + // Uses this event to process mouseDown to check the selection state before it is changed + this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) + { + if (evt.getProperty('eventName') == 'mouseDown' && this.isEnabled()) + { + var me = evt.getProperty('event'); + + if (!mxEvent.isControlDown(me.getEvent()) && !mxEvent.isShiftDown(me.getEvent())) + { + var state = me.getState(); + + if (state != null) + { + // Checks if state was removed in call to stopEditing above + if (this.model.isEdge(state.cell)) + { + start.point = new mxPoint(me.getGraphX(), me.getGraphY()); + start.selected = this.isCellSelected(state.cell); + start.state = state; + start.event = me; + + if (state.text != null && state.text.boundingBox != null && + mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) + { + start.handle = mxEvent.LABEL_HANDLE; + } + else + { + var handler = this.selectionCellsHandler.getHandler(state.cell); + + if (handler != null && handler.bends != null && handler.bends.length > 0) + { + start.handle = handler.getHandleForEvent(me); + } + } + } + } + } + } + })); + + var mouseDown = null; + + this.addMouseListener( + { + mouseDown: function(sender, me) {}, + mouseMove: mxUtils.bind(this, function(sender, me) + { + // Checks if any other handler is active + var handlerMap = this.selectionCellsHandler.handlers.map; + + for (var key in handlerMap) + { + if (handlerMap[key].index != null) + { + return; + } + } + + if (this.isEnabled() && !this.panningHandler.isActive() && !mxEvent.isControlDown(me.getEvent()) && + !mxEvent.isShiftDown(me.getEvent()) && !mxEvent.isAltDown(me.getEvent())) + { + var tol = this.tolerance; + + if (start.point != null && start.state != null && start.event != null) + { + var state = start.state; + + if (Math.abs(start.point.x - me.getGraphX()) > tol || + Math.abs(start.point.y - me.getGraphY()) > tol) + { + // Lazy selection for edges inside groups + if (!this.isCellSelected(state.cell)) + { + this.setSelectionCell(state.cell); + } + + var handler = this.selectionCellsHandler.getHandler(state.cell); + + if (handler != null && handler.bends != null && handler.bends.length > 0) + { + var handle = handler.getHandleForEvent(start.event); + var edgeStyle = this.view.getEdgeStyle(state); + var entity = edgeStyle == mxEdgeStyle.EntityRelation; + + // Handles special case where label was clicked on unselected edge in which + // case the label will be moved regardless of the handle that is returned + if (!start.selected && start.handle == mxEvent.LABEL_HANDLE) + { + handle = start.handle; + } + + if (!entity || handle == 0 || handle == handler.bends.length - 1 || handle == mxEvent.LABEL_HANDLE) + { + // Source or target handle or connected for direct handle access or orthogonal line + // with just two points where the central handle is moved regardless of mouse position + if (handle == mxEvent.LABEL_HANDLE || handle == 0 || state.visibleSourceState != null || + handle == handler.bends.length - 1 || state.visibleTargetState != null) + { + if (!entity && handle != mxEvent.LABEL_HANDLE) + { + var pts = state.absolutePoints; + + // Default case where handles are at corner points handles + // drag of corner as drag of existing point + if (pts != null && ((edgeStyle == null && handle == null) || + edgeStyle == mxEdgeStyle.OrthConnector)) + { + // Does not use handles if they were not initially visible + handle = start.handle; + + if (handle == null) + { + var box = new mxRectangle(start.point.x, start.point.y); + box.grow(mxEdgeHandler.prototype.handleImage.width / 2); + + if (mxUtils.contains(box, pts[0].x, pts[0].y)) + { + // Moves source terminal handle + handle = 0; + } + else if (mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) + { + // Moves target terminal handle + handle = handler.bends.length - 1; + } + else + { + // Checks if edge has no bends + var nobends = edgeStyle != null && (pts.length == 2 || (pts.length == 3 && + ((Math.round(pts[0].x - pts[1].x) == 0 && Math.round(pts[1].x - pts[2].x) == 0) || + (Math.round(pts[0].y - pts[1].y) == 0 && Math.round(pts[1].y - pts[2].y) == 0)))); + + if (nobends) + { + // Moves central handle for straight orthogonal edges + handle = 2; + } + else + { + // Finds and moves vertical or horizontal segment + handle = mxUtils.findNearestSegment(state, start.point.x, start.point.y); + + // Converts segment to virtual handle index + if (edgeStyle == null) + { + handle = mxEvent.VIRTUAL_HANDLE - handle; + } + // Maps segment to handle + else + { + handle += 1; + } + } + } + } + } + + // Creates a new waypoint and starts moving it + if (handle == null) + { + handle = mxEvent.VIRTUAL_HANDLE; + } + } + + handler.start(me.getGraphX(), me.getGraphX(), handle); + start.state = null; + start.event = null; + start.point = null; + start.handle = null; + start.selected = false; + me.consume(); + + // Removes preview rectangle in graph handler + this.graphHandler.reset(); + } + } + else if (entity && (state.visibleSourceState != null || state.visibleTargetState != null)) + { + // Disables moves on entity to make it consistent + this.graphHandler.reset(); + me.consume(); + } + } + } + } + else + { + // Updates cursor for unselected edges under the mouse + var state = me.getState(); + + if (state != null) + { + // Checks if state was removed in call to stopEditing above + if (this.model.isEdge(state.cell)) + { + var cursor = null; + var pts = state.absolutePoints; + + if (pts != null) + { + var box = new mxRectangle(me.getGraphX(), me.getGraphY()); + box.grow(mxEdgeHandler.prototype.handleImage.width / 2); + + if (state.text != null && state.text.boundingBox != null && + mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) + { + cursor = 'move'; + } + else if (mxUtils.contains(box, pts[0].x, pts[0].y) || + mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) + { + cursor = 'pointer'; + } + else if (state.visibleSourceState != null || state.visibleTargetState != null) + { + // Moving is not allowed for entity relation but still indicate hover state + var tmp = this.view.getEdgeStyle(state); + cursor = 'crosshair'; + + if (tmp != mxEdgeStyle.EntityRelation && this.isOrthogonal(state)) + { + var idx = mxUtils.findNearestSegment(state, me.getGraphX(), me.getGraphY()); + + if (idx < pts.length - 1 && idx >= 0) + { + cursor = (Math.round(pts[idx].x - pts[idx + 1].x) == 0) ? + 'col-resize' : 'row-resize'; + } + } + } + } + + if (cursor != null) + { + state.setCursor(cursor); + } + } + } + } + } + }), + mouseUp: mxUtils.bind(this, function(sender, me) + { + start.state = null; + start.event = null; + start.point = null; + start.handle = null; + }) + }); + } + + // HTML entities are displayed as plain text in wrapped plain text labels + this.cellRenderer.getLabelValue = function(state) + { + var result = mxCellRenderer.prototype.getLabelValue.apply(this, arguments); + + if (state.view.graph.isHtmlLabel(state.cell)) + { + if (state.style['html'] != 1) + { + result = mxUtils.htmlEntities(result, false); + } + else + { + result = state.view.graph.sanitizeHtml(result); + } + } + + return result; + }; + + // All code below not available and not needed in embed mode + if (typeof mxVertexHandler !== 'undefined') + { + this.setConnectable(true); + this.setDropEnabled(true); + this.setPanning(true); + this.setTooltips(true); + this.setAllowLoops(true); + this.allowAutoPanning = true; + this.resetEdgesOnConnect = false; + this.constrainChildren = false; + this.constrainRelativeChildren = true; + + // Do not scroll after moving cells + this.graphHandler.scrollOnMove = false; + this.graphHandler.scaleGrid = true; + + // Disables cloning of connection sources by default + this.connectionHandler.setCreateTarget(false); + this.connectionHandler.insertBeforeSource = true; + + // Disables built-in connection starts + this.connectionHandler.isValidSource = function(cell, me) + { + return false; + }; + + // Sets the style to be used when an elbow edge is double clicked + this.alternateEdgeStyle = 'vertical'; + + if (stylesheet == null) + { + this.loadStylesheet(); + } + + // Adds page centers to the guides for moving cells + var graphHandlerGetGuideStates = this.graphHandler.getGuideStates; + this.graphHandler.getGuideStates = function() + { + var result = graphHandlerGetGuideStates.apply(this, arguments); + + // Create virtual cell state for page centers + if (this.graph.pageVisible) + { + var guides = []; + + var pf = this.graph.pageFormat; + var ps = this.graph.pageScale; + var pw = pf.width * ps; + var ph = pf.height * ps; + var t = this.graph.view.translate; + var s = this.graph.view.scale; + + var layout = this.graph.getPageLayout(); + + for (var i = 0; i < layout.width; i++) + { + guides.push(new mxRectangle(((layout.x + i) * pw + t.x) * s, + (layout.y * ph + t.y) * s, pw * s, ph * s)); + } + + for (var j = 1; j < layout.height; j++) + { + guides.push(new mxRectangle((layout.x * pw + t.x) * s, + ((layout.y + j) * ph + t.y) * s, pw * s, ph * s)); + } + + // Page center guides have precedence over normal guides + result = guides.concat(result); + } + + return result; + }; + + // Overrides zIndex for dragElement + mxDragSource.prototype.dragElementZIndex = mxPopupMenu.prototype.zIndex; + + // Overrides color for virtual guides for page centers + mxGuide.prototype.getGuideColor = function(state, horizontal) + { + return (state.cell == null) ? '#ffa500' /* orange */ : mxConstants.GUIDE_COLOR; + }; + + // Changes color of move preview for black backgrounds + this.graphHandler.createPreviewShape = function(bounds) + { + this.previewColor = (this.graph.background == '#000000') ? '#ffffff' : mxGraphHandler.prototype.previewColor; + + return mxGraphHandler.prototype.createPreviewShape.apply(this, arguments); + }; + + // Handles parts of cells by checking if part=1 is in the style and returning the parent + // if the parent is not already in the list of cells. container style is used to disable + // step into swimlanes and dropTarget style is used to disable acting as a drop target. + // LATER: Handle recursive parts + var graphHandlerGetCells = this.graphHandler.getCells; + + this.graphHandler.getCells = function(initialCell) + { + var cells = graphHandlerGetCells.apply(this, arguments); + var newCells = []; + + for (var i = 0; i < cells.length; i++) + { + var cell = this.graph.getCompositeParent(cells[i]); + + if (cell == cells[i]) + { + newCells.push(cells[i]); + } + else if (cell != null && mxUtils.indexOf(cells, cell) < 0) + { + newCells.push(cell); + } + } + + return newCells; + }; + + // Handles parts of cells for drag and drop + var graphHandlerStart = this.graphHandler.start; + + this.graphHandler.start = function(cell, x, y, cells) + { + cell = this.graph.getCompositeParent(cell); + + graphHandlerStart.apply(this, arguments); + }; + + // Handles parts of cells when cloning the source for new connections + this.connectionHandler.createTargetVertex = function(evt, source) + { + source = this.graph.getCompositeParent(source); + + return mxConnectionHandler.prototype.createTargetVertex.apply(this, arguments); + }; + + var rubberband = new mxRubberband(this); + + this.getRubberband = function() + { + return rubberband; + }; + + // Timer-based activation of outline connect in connection handler + var startTime = new Date().getTime(); + var timeOnTarget = 0; + + var connectionHandlerMouseMove = this.connectionHandler.mouseMove; + + this.connectionHandler.mouseMove = function() + { + var prev = this.currentState; + connectionHandlerMouseMove.apply(this, arguments); + + if (prev != this.currentState) + { + startTime = new Date().getTime(); + timeOnTarget = 0; + } + else + { + timeOnTarget = new Date().getTime() - startTime; + } + }; + + // Activates outline connect after 1500ms with touch event or if alt is pressed inside the shape + // outlineConnect=0 is a custom style that means do not connect to strokes inside the shape, + // or in other words, connect to the shape's perimeter if the highlight is under the mouse + // (the name is because the highlight, including all strokes, is called outline in the code) + var connectionHandleIsOutlineConnectEvent = this.connectionHandler.isOutlineConnectEvent; + + this.connectionHandler.isOutlineConnectEvent = function(me) + { + return (this.currentState != null && me.getState() == this.currentState && timeOnTarget > 2000) || + ((this.currentState == null || mxUtils.getValue(this.currentState.style, 'outlineConnect', '1') != '0') && + connectionHandleIsOutlineConnectEvent.apply(this, arguments)); + }; + + // Adds shift+click to toggle selection state + var isToggleEvent = this.isToggleEvent; + this.isToggleEvent = function(evt) + { + return isToggleEvent.apply(this, arguments) || (!mxClient.IS_CHROMEOS && mxEvent.isShiftDown(evt)); + }; + + // Workaround for Firefox where first mouse down is received + // after tap and hold if scrollbars are visible, which means + // start rubberband immediately if no cell is under mouse. + var isForceRubberBandEvent = rubberband.isForceRubberbandEvent; + rubberband.isForceRubberbandEvent = function(me) + { + return (isForceRubberBandEvent.apply(this, arguments) && !mxEvent.isShiftDown(me.getEvent()) && + !mxEvent.isControlDown(me.getEvent())) || (mxClient.IS_CHROMEOS && mxEvent.isShiftDown(me.getEvent())) || + (mxUtils.hasScrollbars(this.graph.container) && mxClient.IS_FF && + mxClient.IS_WIN && me.getState() == null && mxEvent.isTouchEvent(me.getEvent())); + }; + + // Shows hand cursor while panning + var prevCursor = null; + + this.panningHandler.addListener(mxEvent.PAN_START, mxUtils.bind(this, function() + { + if (this.isEnabled()) + { + prevCursor = this.container.style.cursor; + this.container.style.cursor = 'move'; + } + })); + + this.panningHandler.addListener(mxEvent.PAN_END, mxUtils.bind(this, function() + { + if (this.isEnabled()) + { + this.container.style.cursor = prevCursor; + } + })); + + this.popupMenuHandler.autoExpand = true; + + this.popupMenuHandler.isSelectOnPopup = function(me) + { + return mxEvent.isMouseEvent(me.getEvent()); + }; + + // Handles links if graph is read-only or cell is locked + var click = this.click; + this.click = function(me) + { + var locked = me.state == null && me.sourceState != null && + this.isCellLocked(me.sourceState.cell); + + if ((!this.isEnabled() || locked) && !me.isConsumed()) + { + var cell = (locked) ? me.sourceState.cell : me.getCell(); + + if (cell != null) + { + var link = this.getClickableLinkForCell(cell); + + if (link != null) + { + if (this.isCustomLink(link)) + { + this.customLinkClicked(link); + } + else + { + this.openLink(link); + } + } + } + + if (this.isEnabled() && locked) + { + this.clearSelection(); + } + } + else + { + return click.apply(this, arguments); + } + }; + + // Redirects tooltips for locked cells + this.tooltipHandler.getStateForEvent = function(me) + { + return me.sourceState; + }; + + // Redirects cursor for locked cells + var getCursorForMouseEvent = this.getCursorForMouseEvent; + this.getCursorForMouseEvent = function(me) + { + var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell); + + return this.getCursorForCell((locked) ? me.sourceState.cell : me.getCell()); + }; + + // Shows pointer cursor for clickable cells with links + // ie. if the graph is disabled and cells cannot be selected + var getCursorForCell = this.getCursorForCell; + this.getCursorForCell = function(cell) + { + if (!this.isEnabled() || this.isCellLocked(cell)) + { + var link = this.getClickableLinkForCell(cell); + + if (link != null) + { + return 'pointer'; + } + else if (this.isCellLocked(cell)) + { + return 'default'; + } + } + + return getCursorForCell.apply(this, arguments); + }; + + // Changes rubberband selection to be recursive + this.selectRegion = function(rect, evt) + { + var cells = this.getAllCells(rect.x, rect.y, rect.width, rect.height); + this.selectCellsForEvent(cells, evt); + + return cells; + }; + + // Recursive implementation for rubberband selection + this.getAllCells = function(x, y, width, height, parent, result) + { + result = (result != null) ? result : []; + + if (width > 0 || height > 0) + { + var model = this.getModel(); + var right = x + width; + var bottom = y + height; + + if (parent == null) + { + parent = this.getCurrentRoot(); + + if (parent == null) + { + parent = model.getRoot(); + } + } + + if (parent != null) + { + var childCount = model.getChildCount(parent); + + for (var i = 0; i < childCount; i++) + { + var cell = model.getChildAt(parent, i); + var state = this.view.getState(cell); + + if (state != null && this.isCellVisible(cell) && mxUtils.getValue(state.style, 'locked', '0') != '1') + { + var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0; + var box = state; + + if (deg != 0) + { + box = mxUtils.getBoundingBox(box, deg); + } + + if ((model.isEdge(cell) || model.isVertex(cell)) && + box.x >= x && box.y + box.height <= bottom && + box.y >= y && box.x + box.width <= right) + { + result.push(cell); + } + + this.getAllCells(x, y, width, height, cell, result); + } + } + } + } + + return result; + }; + + // Never removes cells from parents that are being moved + var graphHandlerShouldRemoveCellsFromParent = this.graphHandler.shouldRemoveCellsFromParent; + this.graphHandler.shouldRemoveCellsFromParent = function(parent, cells, evt) + { + if (this.graph.isCellSelected(parent)) + { + return false; + } + + return graphHandlerShouldRemoveCellsFromParent.apply(this, arguments); + }; + + // Unlocks all cells + this.isCellLocked = function(cell) + { + var pState = this.view.getState(cell); + + while (pState != null) + { + if (mxUtils.getValue(pState.style, 'locked', '0') == '1') + { + return true; + } + + pState = this.view.getState(this.model.getParent(pState.cell)); + } + + return false; + }; + + var tapAndHoldSelection = null; + + // Uses this event to process mouseDown to check the selection state before it is changed + this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) + { + if (evt.getProperty('eventName') == 'mouseDown') + { + var me = evt.getProperty('event'); + var state = me.getState(); + + if (state != null && !this.isSelectionEmpty() && !this.isCellSelected(state.cell)) + { + tapAndHoldSelection = this.getSelectionCells(); + } + else + { + tapAndHoldSelection = null; + } + } + })); + + // Tap and hold on background starts rubberband for multiple selected + // cells the cell associated with the event is deselected + this.addListener(mxEvent.TAP_AND_HOLD, mxUtils.bind(this, function(sender, evt) + { + if (!mxEvent.isMultiTouchEvent(evt)) + { + var me = evt.getProperty('event'); + var cell = evt.getProperty('cell'); + + if (cell == null) + { + var pt = mxUtils.convertPoint(this.container, + mxEvent.getClientX(me), mxEvent.getClientY(me)); + rubberband.start(pt.x, pt.y); + } + else if (tapAndHoldSelection != null) + { + this.addSelectionCells(tapAndHoldSelection); + } + else if (this.getSelectionCount() > 1 && this.isCellSelected(cell)) + { + this.removeSelectionCell(cell); + } + + // Blocks further processing of the event + tapAndHoldSelection = null; + evt.consume(); + } + })); + + // On connect the target is selected and we clone the cell of the preview edge for insert + this.connectionHandler.selectCells = function(edge, target) + { + this.graph.setSelectionCell(target || edge); + }; + + // Shows connection points only if cell not selected + this.connectionHandler.constraintHandler.isStateIgnored = function(state, source) + { + return source && state.view.graph.isCellSelected(state.cell); + }; + + // Updates constraint handler if the selection changes + this.selectionModel.addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + var ch = this.connectionHandler.constraintHandler; + + if (ch.currentFocus != null && ch.isStateIgnored(ch.currentFocus, true)) + { + ch.currentFocus = null; + ch.constraints = null; + ch.destroyIcons(); + } + + ch.destroyFocusHighlight(); + })); + + // Initializes touch interface + if (Graph.touchStyle) + { + this.initTouch(); + } + + /** + * Adds locking + */ + var graphUpdateMouseEvent = this.updateMouseEvent; + this.updateMouseEvent = function(me) + { + me = graphUpdateMouseEvent.apply(this, arguments); + + if (me.state != null && this.isCellLocked(me.getCell())) + { + me.state = null; + } + + return me; + }; + } + + //Create a unique offset object for each graph instance. + this.currentTranslate = new mxPoint(0, 0); +}; + +/** + * Specifies if the touch UI should be used (cannot detect touch in FF so always on for Windows/Linux) + */ +Graph.touchStyle = mxClient.IS_TOUCH || (mxClient.IS_FF && mxClient.IS_WIN) || navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 || window.urlParams == null || urlParams['touch'] == '1'; + +/** + * Shortcut for capability check. + */ +Graph.fileSupport = window.File != null && window.FileReader != null && window.FileList != null && + (window.urlParams == null || urlParams['filesupport'] != '0'); + +/** + * Default size for line jumps. + */ +Graph.lineJumpsEnabled = true; + +/** + * Default size for line jumps. + */ +Graph.defaultJumpSize = 6; + +/** + * Minimum width for table columns. + */ +Graph.minTableColumnWidth = 20; + +/** + * Minimum height for table rows. + */ +Graph.minTableRowHeight = 20; + +/** + * Text for foreign object warning. + */ +Graph.foreignObjectWarningText = 'Viewer does not support full SVG 1.1'; + +/** + * Link for foreign object warning. + */ +Graph.foreignObjectWarningLink = 'https://desk.draw.io/support/solutions/articles/16000042487'; + +/** + * Helper function for creating SVG data URI. + */ +Graph.createSvgImage = function(w, h, data, coordWidth, coordHeight) +{ + var tmp = unescape(encodeURIComponent( + '' + + '' + data + '')); + + return new mxImage('data:image/svg+xml;base64,' + ((window.btoa) ? btoa(tmp) : Base64.encode(tmp, true)), w, h) +}; + +/** + * Removes all illegal control characters with ASCII code <32 except TAB, LF + * and CR. + */ +Graph.zapGremlins = function(text) +{ + var checked = []; + + for (var i = 0; i < text.length; i++) + { + var code = text.charCodeAt(i); + + // Removes all control chars except TAB, LF and CR + if ((code >= 32 || code == 9 || code == 10 || code == 13) && + code != 0xFFFF && code != 0xFFFE) + { + checked.push(text.charAt(i)); + } + } + + return checked.join(''); +}; + +/** + * Turns the given string into an array. + */ +Graph.stringToBytes = function(str) +{ + var arr = new Array(str.length); + + for (var i = 0; i < str.length; i++) + { + arr[i] = str.charCodeAt(i); + } + + return arr; +}; + +/** + * Turns the given array into a string. + */ +Graph.bytesToString = function(arr) +{ + var result = new Array(arr.length); + + for (var i = 0; i < arr.length; i++) + { + result[i] = String.fromCharCode(arr[i]); + } + + return result.join(''); +}; + +/** + * Returns a base64 encoded version of the compressed outer XML of the given node. + */ +Graph.compressNode = function(node, checked) +{ + var xml = mxUtils.getXml(node); + + return Graph.compress((checked) ? xml : Graph.zapGremlins(xml)); +}; + +/** + * Returns a base64 encoded version of the compressed string. + */ +Graph.compress = function(data, deflate) +{ + if (data == null || data.length == 0 || typeof(pako) === 'undefined') + { + return data; + } + else + { + var tmp = (deflate) ? pako.deflate(encodeURIComponent(data), {to: 'string'}) : + pako.deflateRaw(encodeURIComponent(data), {to: 'string'}); + + return (window.btoa) ? btoa(tmp) : Base64.encode(tmp, true); + } +}; + +/** + * Returns a decompressed version of the base64 encoded string. + */ +Graph.decompress = function(data, inflate, checked) +{ + if (data == null || data.length == 0 || typeof(pako) === 'undefined') + { + return data; + } + else + { + var tmp = (window.atob) ? atob(data) : Base64.decode(data, true); + + var inflated = decodeURIComponent((inflate) ? + pako.inflate(tmp, {to: 'string'}) : + pako.inflateRaw(tmp, {to: 'string'})); + + return (checked) ? inflated : Graph.zapGremlins(inflated); + } +}; + +/** + * Graph inherits from mxGraph. + */ +mxUtils.extend(Graph, mxGraph); + +/** + * Allows all values in fit. + */ +Graph.prototype.minFitScale = null; + +/** + * Allows all values in fit. + */ +Graph.prototype.maxFitScale = null; + +/** + * Sets the policy for links. Possible values are "self" to replace any framesets, + * "blank" to load the URL in and "auto" (default). + */ +Graph.prototype.linkPolicy = (urlParams['target'] == 'frame') ? 'blank' : (urlParams['target'] || 'auto'); + +/** + * Target for links that open in a new window. Default is _blank. + */ +Graph.prototype.linkTarget = (urlParams['target'] == 'frame') ? '_self' : '_blank'; + +/** + * Value to the rel attribute of links. Default is 'nofollow noopener noreferrer'. + * NOTE: There are security implications when this is changed and if noopener is removed, + * then must be overridden to allow for the opener to be set by default. + */ +Graph.prototype.linkRelation = 'nofollow noopener noreferrer'; + +/** + * Scrollbars are enabled on non-touch devices (not including Firefox because touch events + * cannot be detected in Firefox, see above). + */ +Graph.prototype.defaultScrollbars = !mxClient.IS_IOS; + +/** + * Specifies if the page should be visible for new files. Default is true. + */ +Graph.prototype.defaultPageVisible = true; + +/** + * Specifies if the app should run in chromeless mode. Default is false. + * This default is only used if the contructor argument is null. + */ +Graph.prototype.lightbox = false; + +/** + * + */ +Graph.prototype.defaultPageBackgroundColor = '#ffffff'; + +/** + * + */ +Graph.prototype.defaultPageBorderColor = '#ffffff'; + +/** + * Specifies the size of the size for "tiles" to be used for a graph with + * scrollbars but no visible background page. A good value is large + * enough to reduce the number of repaints that is caused for auto- + * translation, which depends on this value, and small enough to give + * a small empty buffer around the graph. Default is 400x400. + */ +Graph.prototype.scrollTileSize = new mxRectangle(0, 0, 400, 400); + +/** + * Overrides the background color and paints a transparent background. + */ +Graph.prototype.transparentBackground = true; + +/** + * Sets global constants. + */ +Graph.prototype.selectParentAfterDelete = false; + +/** + * Sets the default target for all links in cells. + */ +Graph.prototype.defaultEdgeLength = 80; + +/** + * Disables move of bends/segments without selecting. + */ +Graph.prototype.edgeMode = false; + +/** + * Allows all values in fit. + */ +Graph.prototype.connectionArrowsEnabled = true; + +/** + * Specifies the regular expression for matching placeholders. + */ +Graph.prototype.placeholderPattern = new RegExp('%(date\{.*\}|[^%^\{^\}]+)%', 'g'); + +/** + * Specifies the regular expression for matching placeholders. + */ +Graph.prototype.absoluteUrlPattern = new RegExp('^(?:[a-z]+:)?//', 'i'); + +/** + * Specifies the default name for the theme. Default is 'default'. + */ +Graph.prototype.defaultThemeName = 'default'; + +/** + * Specifies the default name for the theme. Default is 'default'. + */ +Graph.prototype.defaultThemes = {}; + +/** + * Base URL for relative links. + */ +Graph.prototype.baseUrl = (urlParams['base'] != null) ? + decodeURIComponent(urlParams['base']) : + (((window != window.top) ? document.referrer : + document.location.toString()).split('#')[0]); + +/** + * Specifies if the label should be edited after an insert. + */ +Graph.prototype.editAfterInsert = false; + +/** + * Defines the built-in properties to be ignored in tooltips. + */ +Graph.prototype.builtInProperties = ['label', 'tooltip', 'placeholders', 'placeholder']; + +/** + * Defines if the graph is part of an EditorUi. If this is false the graph can + * be used in an EditorUi instance but will not have a UI added, functions + * overridden or event handlers added. + */ +Graph.prototype.standalone = false; + +/** + * Installs child layout styles. + */ +Graph.prototype.init = function(container) +{ + mxGraph.prototype.init.apply(this, arguments); + + // Intercepts links with no target attribute and opens in new window + this.cellRenderer.initializeLabel = function(state, shape) + { + mxCellRenderer.prototype.initializeLabel.apply(this, arguments); + + // Checks tolerance for clicks on links + var tol = state.view.graph.tolerance; + var handleClick = true; + var first = null; + + var down = mxUtils.bind(this, function(evt) + { + handleClick = true; + first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + }); + + var move = mxUtils.bind(this, function(evt) + { + handleClick = handleClick && first != null && + Math.abs(first.x - mxEvent.getClientX(evt)) < tol && + Math.abs(first.y - mxEvent.getClientY(evt)) < tol; + }); + + var up = mxUtils.bind(this, function(evt) + { + if (handleClick) + { + var elt = mxEvent.getSource(evt) + + while (elt != null && elt != shape.node) + { + if (elt.nodeName.toLowerCase() == 'a') + { + state.view.graph.labelLinkClicked(state, elt, evt); + break; + } + + elt = elt.parentNode; + } + } + }); + + mxEvent.addGestureListeners(shape.node, down, move, up); + mxEvent.addListener(shape.node, 'click', function(evt) + { + mxEvent.consume(evt); + }); + }; + + this.initLayoutManager(); +}; + +/** + * Implements zoom and offset via CSS transforms. This is currently only used + * in read-only as there are fewer issues with the mxCellState not being scaled + * and translated. + * + * KNOWN ISSUES TO FIX: + * - Apply CSS transforms to HTML labels in IE11 + */ +(function() +{ + /** + * Uses CSS transforms for scale and translate. + */ + Graph.prototype.useCssTransforms = false; + + /** + * Contains the scale. + */ + Graph.prototype.currentScale = 1; + + /** + * Contains the offset. + */ + Graph.prototype.currentTranslate = new mxPoint(0, 0); + + /** + * Returns true if fast zoom preview should be used. + */ + Graph.prototype.isFastZoomEnabled = function() + { + return urlParams['zoom'] != 'nocss' && !mxClient.NO_FO && !mxClient.IS_EDGE && + !this.useCssTransforms && this.isCssTransformsSupported(); + }; + + /** + * Only foreignObject supported for now (no IE11). Safari disabled as it ignores + * overflow visible on foreignObject in negative space (lightbox and viewer). + */ + Graph.prototype.isCssTransformsSupported = function() + { + return this.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO && + (!this.lightbox || !mxClient.IS_SF); + }; + + /** + * Function: getCellAt + * + * Needs to modify original method for recursive call. + */ + Graph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn) + { + if (this.useCssTransforms) + { + x = x / this.currentScale - this.currentTranslate.x; + y = y / this.currentScale - this.currentTranslate.y; + } + + return this.getScaledCellAt.apply(this, arguments); + }; + + /** + * Function: getScaledCellAt + * + * Overridden for recursion. + */ + Graph.prototype.getScaledCellAt = function(x, y, parent, vertices, edges, ignoreFn) + { + vertices = (vertices != null) ? vertices : true; + edges = (edges != null) ? edges : true; + + if (parent == null) + { + parent = this.getCurrentRoot(); + + if (parent == null) + { + parent = this.getModel().getRoot(); + } + } + + if (parent != null) + { + var childCount = this.model.getChildCount(parent); + + for (var i = childCount - 1; i >= 0; i--) + { + var cell = this.model.getChildAt(parent, i); + var result = this.getScaledCellAt(x, y, cell, vertices, edges, ignoreFn); + + if (result != null) + { + return result; + } + else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) || + vertices && this.model.isVertex(cell))) + { + var state = this.view.getState(cell); + + if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) && + this.intersects(state, x, y)) + { + return cell; + } + } + } + } + + return null; + }; + + /** + * Returns if the child cells of the given vertex cell state should be resized. + */ + Graph.prototype.isRecursiveVertexResize = function(state) + { + return !this.isSwimlane(state.cell) && this.model.getChildCount(state.cell) > 0 && + !this.isCellCollapsed(state.cell) && mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' && + mxUtils.getValue(state.style, 'childLayout', null) == null; + } + + /** + * Returns the first parent that is not a part. + */ + Graph.prototype.isPart = function(cell) + { + return (!this.model.isVertex(cell)) ? false : + mxUtils.getValue(this.getCurrentCellStyle(cell), 'part', '0') == '1'; + }; + + /** + * Returns the first parent that is not a part. + */ + Graph.prototype.getCompositeParent = function(cell) + { + while (this.isPart(cell)) + { + cell = this.model.getParent(cell); + } + + return cell; + }; + + /** + * Function: repaint + * + * Updates the highlight after a change of the model or view. + */ + mxCellHighlight.prototype.getStrokeWidth = function(state) + { + var s = this.strokeWidth; + + if (this.graph.useCssTransforms) + { + s /= this.graph.currentScale; + } + + return s; + }; + + /** + * Function: getGraphBounds + * + * Overrides getGraphBounds to use bounding box from SVG. + */ + mxGraphView.prototype.getGraphBounds = function() + { + var b = this.graphBounds; + + if (this.graph.useCssTransforms) + { + var t = this.graph.currentTranslate; + var s = this.graph.currentScale; + + b = new mxRectangle( + (b.x + t.x) * s, (b.y + t.y) * s, + b.width * s, b.height * s); + } + + return b; + }; + + /** + * Function: viewStateChanged + * + * Overrides to bypass full cell tree validation. + * TODO: Check if this improves performance + */ + mxGraphView.prototype.viewStateChanged = function() + { + if (this.graph.useCssTransforms) + { + this.validate(); + this.graph.sizeDidChange(); + } + else + { + this.revalidate(); + this.graph.sizeDidChange(); + } + }; + + /** + * Function: validate + * + * Overrides validate to normalize validation view state and pass + * current state to CSS transform. + */ + var graphViewValidate = mxGraphView.prototype.validate; + + mxGraphView.prototype.validate = function(cell) + { + if (this.graph.useCssTransforms) + { + this.graph.currentScale = this.scale; + this.graph.currentTranslate.x = this.translate.x; + this.graph.currentTranslate.y = this.translate.y; + + this.scale = 1; + this.translate.x = 0; + this.translate.y = 0; + } + + graphViewValidate.apply(this, arguments); + + if (this.graph.useCssTransforms) + { + this.graph.updateCssTransform(); + + this.scale = this.graph.currentScale; + this.translate.x = this.graph.currentTranslate.x; + this.translate.y = this.graph.currentTranslate.y; + } + }; + + /** + * Function: updateCssTransform + * + * Zooms out of the graph by . + */ + Graph.prototype.updateCssTransform = function() + { + var temp = this.view.getDrawPane(); + + if (temp != null) + { + var g = temp.parentNode; + + if (!this.useCssTransforms) + { + g.removeAttribute('transformOrigin'); + g.removeAttribute('transform'); + } + else + { + var prev = g.getAttribute('transform'); + g.setAttribute('transformOrigin', '0 0'); + var s = Math.round(this.currentScale * 100) / 100; + var dx = Math.round(this.currentTranslate.x * 100) / 100; + var dy = Math.round(this.currentTranslate.y * 100) / 100; + g.setAttribute('transform', 'scale(' + s + ',' + s + ')' + + 'translate(' + dx + ',' + dy + ')'); + + // Applies workarounds only if translate has changed + if (prev != g.getAttribute('transform')) + { + try + { + // Applies transform to labels outside of the SVG DOM + // Excluded via isCssTransformsSupported + // if (mxClient.NO_FO) + // { + // var transform = 'scale(' + this.currentScale + ')' + 'translate(' + + // this.currentTranslate.x + 'px,' + this.currentTranslate.y + 'px)'; + // + // this.view.states.visit(mxUtils.bind(this, function(cell, state) + // { + // if (state.text != null && state.text.node != null) + // { + // // Stores initial CSS transform that is used for the label alignment + // if (state.text.originalTransform == null) + // { + // state.text.originalTransform = state.text.node.style.transform; + // } + // + // state.text.node.style.transform = transform + state.text.originalTransform; + // } + // })); + // } + // Workaround for https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4320441/ + if (mxClient.IS_EDGE) + { + // Recommended workaround is to do this on all + // foreignObjects, but this seems to be faster + var val = g.style.display; + g.style.display = 'none'; + g.getBBox(); + g.style.display = val; + } + } + catch (e) + { + // ignore + } + } + } + } + }; + + var graphViewValidateBackgroundPage = mxGraphView.prototype.validateBackgroundPage; + + mxGraphView.prototype.validateBackgroundPage = function() + { + var useCssTranforms = this.graph.useCssTransforms, scale = this.scale, + translate = this.translate; + + if (useCssTranforms) + { + this.scale = this.graph.currentScale; + this.translate = this.graph.currentTranslate; + } + + graphViewValidateBackgroundPage.apply(this, arguments); + + if (useCssTranforms) + { + this.scale = scale; + this.translate = translate; + } + }; + + var graphUpdatePageBreaks = mxGraph.prototype.updatePageBreaks; + + mxGraph.prototype.updatePageBreaks = function(visible, width, height) + { + var useCssTranforms = this.useCssTransforms, scale = this.view.scale, + translate = this.view.translate; + + if (useCssTranforms) + { + this.view.scale = 1; + this.view.translate = new mxPoint(0, 0); + this.useCssTransforms = false; + } + + graphUpdatePageBreaks.apply(this, arguments); + + if (useCssTranforms) + { + this.view.scale = scale; + this.view.translate = translate; + this.useCssTransforms = true; + } + }; + +})(); + +/** + * Sets the XML node for the current diagram. + */ +Graph.prototype.isLightboxView = function() +{ + return this.lightbox; +}; + +/** + * Sets the XML node for the current diagram. + */ +Graph.prototype.isViewer = function() +{ + return false; +}; + +/** + * Installs automatic layout via styles + */ +Graph.prototype.labelLinkClicked = function(state, elt, evt) +{ + var href = elt.getAttribute('href'); + + if (href != null && !this.isCustomLink(href) && (mxEvent.isLeftMouseButton(evt) && + !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)) + { + if (!this.isEnabled() || this.isCellLocked(state.cell)) + { + var target = this.isBlankLink(href) ? this.linkTarget : '_top'; + this.openLink(this.getAbsoluteUrl(href), target); + } + + mxEvent.consume(evt); + } +}; + +/** + * Returns the size of the page format scaled with the page size. + */ +Graph.prototype.openLink = function(href, target, allowOpener) +{ + var result = window; + + try + { + // Workaround for blocking in same iframe + if (target == '_self' && window != window.top) + { + window.location.href = href; + } + else + { + // Avoids page reload for anchors (workaround for IE but used everywhere) + if (href.substring(0, this.baseUrl.length) == this.baseUrl && + href.charAt(this.baseUrl.length) == '#' && + target == '_top' && window == window.top) + { + var hash = href.split('#')[1]; + + // Forces navigation if on same hash + if (window.location.hash == '#' + hash) + { + window.location.hash = ''; + } + + window.location.hash = hash; + } + else + { + result = window.open(href, (target != null) ? target : '_blank'); + + if (result != null && !allowOpener) + { + result.opener = null; + } + } + } + } + catch (e) + { + // ignores permission denied + } + + return result; +}; + +/** + * Adds support for page links. + */ +Graph.prototype.getLinkTitle = function(href) +{ + return href.substring(href.lastIndexOf('/') + 1); +}; + +/** + * Adds support for page links. + */ +Graph.prototype.isCustomLink = function(href) +{ + return href.substring(0, 5) == 'data:'; +}; + +/** + * Adds support for page links. + */ +Graph.prototype.customLinkClicked = function(link) +{ + return false; +}; + +/** + * Returns true if the given href references an external protocol that + * should never open in a new window. Default returns true for mailto. + */ +Graph.prototype.isExternalProtocol = function(href) +{ + return href.substring(0, 7) === 'mailto:'; +}; + +/** + * Hook for links to open in same window. Default returns true for anchors, + * links to same domain or if target == 'self' in the config. + */ +Graph.prototype.isBlankLink = function(href) +{ + return !this.isExternalProtocol(href) && + (this.linkPolicy === 'blank' || + (this.linkPolicy !== 'self' && + !this.isRelativeUrl(href) && + href.substring(0, this.domainUrl.length) !== this.domainUrl)); +}; + +/** + * + */ +Graph.prototype.isRelativeUrl = function(url) +{ + return url != null && !this.absoluteUrlPattern.test(url) && + url.substring(0, 5) !== 'data:' && + !this.isExternalProtocol(url); +}; + +/** + * + */ +Graph.prototype.getAbsoluteUrl = function(url) +{ + if (url != null && this.isRelativeUrl(url)) + { + if (url.charAt(0) == '#') + { + url = this.baseUrl + url; + } + else if (url.charAt(0) == '/') + { + url = this.domainUrl + url; + } + else + { + url = this.domainPathUrl + url; + } + } + + return url; +}; + +/** + * Installs automatic layout via styles + */ +Graph.prototype.initLayoutManager = function() +{ + this.layoutManager = new mxLayoutManager(this); + + // Using shared instances for table layouts + var rowLayout = new TableRowLayout(this); + var tableLayout = new TableLayout(this); + + this.layoutManager.getLayout = function(cell, eventName) + { + // Workaround for possible invalid style after change and before view validation + if (eventName != mxEvent.BEGIN_UPDATE) + { + var style = this.graph.getCellStyle(cell); + + if (style != null) + { + if (style['childLayout'] == 'stackLayout') + { + var stackLayout = new mxStackLayout(this.graph, true); + stackLayout.resizeParentMax = mxUtils.getValue(style, 'resizeParentMax', '1') == '1'; + stackLayout.horizontal = mxUtils.getValue(style, 'horizontalStack', '1') == '1'; + stackLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + stackLayout.resizeLast = mxUtils.getValue(style, 'resizeLast', '0') == '1'; + stackLayout.spacing = style['stackSpacing'] || stackLayout.spacing; + stackLayout.border = style['stackBorder'] || stackLayout.border; + stackLayout.marginLeft = style['marginLeft'] || 0; + stackLayout.marginRight = style['marginRight'] || 0; + stackLayout.marginTop = style['marginTop'] || 0; + stackLayout.marginBottom = style['marginBottom'] || 0; + stackLayout.fill = true; + + return stackLayout; + } + else if (style['childLayout'] == 'treeLayout') + { + var treeLayout = new mxCompactTreeLayout(this.graph); + treeLayout.horizontal = mxUtils.getValue(style, 'horizontalTree', '1') == '1'; + treeLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + treeLayout.groupPadding = mxUtils.getValue(style, 'parentPadding', 20); + treeLayout.levelDistance = mxUtils.getValue(style, 'treeLevelDistance', 30); + treeLayout.maintainParentLocation = true; + treeLayout.edgeRouting = false; + treeLayout.resetEdges = false; + + return treeLayout; + } + else if (style['childLayout'] == 'flowLayout') + { + var flowLayout = new mxHierarchicalLayout(this.graph, mxUtils.getValue(style, + 'flowOrientation', mxConstants.DIRECTION_EAST)); + flowLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + flowLayout.parentBorder = mxUtils.getValue(style, 'parentPadding', 20); + flowLayout.maintainParentLocation = true; + + // Special undocumented styles for changing the hierarchical + flowLayout.intraCellSpacing = mxUtils.getValue(style, 'intraCellSpacing', + mxHierarchicalLayout.prototype.intraCellSpacing); + flowLayout.interRankCellSpacing = mxUtils.getValue(style, 'interRankCellSpacing', + mxHierarchicalLayout.prototype.interRankCellSpacing); + flowLayout.interHierarchySpacing = mxUtils.getValue(style, 'interHierarchySpacing', + mxHierarchicalLayout.prototype.interHierarchySpacing); + flowLayout.parallelEdgeSpacing = mxUtils.getValue(style, 'parallelEdgeSpacing', + mxHierarchicalLayout.prototype.parallelEdgeSpacing); + + return flowLayout; + } + else if (style['childLayout'] == 'circleLayout') + { + return new mxCircleLayout(this.graph); + } + else if (style['childLayout'] == 'organicLayout') + { + return new mxFastOrganicLayout(this.graph); + } + else if (this.graph.isTableRow(cell) || + this.graph.isTableCell(cell)) + { + return rowLayout; + } + else if (this.graph.isTable(cell)) + { + return tableLayout; + } + } + } + + return null; + }; +}; + +/** + * Returns the size of the page format scaled with the page size. + */ +Graph.prototype.getPageSize = function() +{ + return (this.pageVisible) ? new mxRectangle(0, 0, this.pageFormat.width * this.pageScale, + this.pageFormat.height * this.pageScale) : this.scrollTileSize; +}; + +/** + * Returns a rectangle describing the position and count of the + * background pages, where x and y are the position of the top, + * left page and width and height are the vertical and horizontal + * page count. + */ +Graph.prototype.getPageLayout = function() +{ + var size = this.getPageSize(); + var bounds = this.getGraphBounds(); + + if (bounds.width == 0 || bounds.height == 0) + { + return new mxRectangle(0, 0, 1, 1); + } + else + { + var x0 = Math.floor(Math.ceil(bounds.x / this.view.scale - + this.view.translate.x) / size.width); + var y0 = Math.floor(Math.ceil(bounds.y / this.view.scale - + this.view.translate.y) / size.height); + var w0 = Math.ceil((Math.floor((bounds.x + bounds.width) / this.view.scale) - + this.view.translate.x) / size.width) - x0; + var h0 = Math.ceil((Math.floor((bounds.y + bounds.height) / this.view.scale) - + this.view.translate.y) / size.height) - y0; + + return new mxRectangle(x0, y0, w0, h0); + } +}; + +/** + * Sanitizes the given HTML markup. + */ +Graph.prototype.sanitizeHtml = function(value, editing) +{ + // Uses https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer + // NOTE: Original minimized sanitizer was modified to support + // data URIs for images, mailto and special data:-links. + // LATER: Add MathML to whitelisted tags + function urlX(link) + { + if (link != null && link.toString().toLowerCase().substring(0, 11) !== 'javascript:') + { + return link; + } + + return null; + }; + function idX(id) { return id }; + + return html_sanitize(value, urlX, idX); +}; + +/** + * Revalidates all cells with placeholders in the current graph model. + */ +Graph.prototype.updatePlaceholders = function() +{ + var model = this.model; + var validate = false; + + for (var key in this.model.cells) + { + var cell = this.model.cells[key]; + + if (this.isReplacePlaceholders(cell)) + { + this.view.invalidate(cell, false, false); + validate = true; + } + } + + if (validate) + { + this.view.validate(); + } +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.isReplacePlaceholders = function(cell) +{ + return cell.value != null && typeof(cell.value) == 'object' && + cell.value.getAttribute('placeholders') == '1'; +}; + +/** + * Returns true if the given mouse wheel event should be used for zooming. This + * is invoked if no dialogs are showing and returns true with Alt or Control + * (or cmd in macOS only) is pressed. + */ +Graph.prototype.isZoomWheelEvent = function(evt) +{ + return mxEvent.isAltDown(evt) || (mxEvent.isMetaDown(evt) && mxClient.IS_MAC) || + mxEvent.isControlDown(evt); +}; + +/** + * Returns true if the given scroll wheel event should be used for scrolling. + */ +Graph.prototype.isScrollWheelEvent = function(evt) +{ + return !this.isZoomWheelEvent(evt); +}; + +/** + * Adds Alt+click to select cells behind cells (Shift+Click on Chrome OS). + */ +Graph.prototype.isTransparentClickEvent = function(evt) +{ + return mxEvent.isAltDown(evt) || (mxClient.IS_CHROMEOS && mxEvent.isShiftDown(evt)); +}; + +/** + * Adds ctrl+shift+connect to disable connections. + */ +Graph.prototype.isIgnoreTerminalEvent = function(evt) +{ + return mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt); +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.isSplitTarget = function(target, cells, evt) +{ + return !this.model.isEdge(cells[0]) && + !mxEvent.isAltDown(evt) && !mxEvent.isShiftDown(evt) && + mxGraph.prototype.isSplitTarget.apply(this, arguments); +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.getLabel = function(cell) +{ + var result = mxGraph.prototype.getLabel.apply(this, arguments); + + if (result != null && this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') == null) + { + result = this.replacePlaceholders(cell, result); + } + + return result; +}; + +/** + * Adds labelMovable style. + */ +Graph.prototype.isLabelMovable = function(cell) +{ + var style = this.getCurrentCellStyle(cell); + + return !this.isCellLocked(cell) && + ((this.model.isEdge(cell) && this.edgeLabelsMovable) || + (this.model.isVertex(cell) && (this.vertexLabelsMovable || + mxUtils.getValue(style, 'labelMovable', '0') == '1'))); +}; + +/** + * Adds event if grid size is changed. + */ +Graph.prototype.setGridSize = function(value) +{ + this.gridSize = value; + this.fireEvent(new mxEventObject('gridSizeChanged')); +}; + +/** + * Function: getClickableLinkForCell + * + * Returns the first non-null link for the cell or its ancestors. + * + * Parameters: + * + * cell - whose link should be returned. + */ +Graph.prototype.getClickableLinkForCell = function(cell) +{ + do + { + var link = this.getLinkForCell(cell); + + if (link != null) + { + return link; + } + + cell = this.model.getParent(cell); + } while (cell != null); + + return null; +}; + +/** + * Private helper method. + */ +Graph.prototype.getGlobalVariable = function(name) +{ + var val = null; + + if (name == 'date') + { + val = new Date().toLocaleDateString(); + } + else if (name == 'time') + { + val = new Date().toLocaleTimeString(); + } + else if (name == 'timestamp') + { + val = new Date().toLocaleString(); + } + else if (name.substring(0, 5) == 'date{') + { + var fmt = name.substring(5, name.length - 1); + val = this.formatDate(new Date(), fmt); + } + + return val; +}; + +/** + * Formats a date, see http://blog.stevenlevithan.com/archives/date-time-format + */ +Graph.prototype.formatDate = function(date, mask, utc) +{ + // LATER: Cache regexs + if (this.dateFormatCache == null) + { + this.dateFormatCache = { + i18n: { + dayNames: [ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" + ], + monthNames: [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + ] + }, + + masks: { + "default": "ddd mmm dd yyyy HH:MM:ss", + shortDate: "m/d/yy", + mediumDate: "mmm d, yyyy", + longDate: "mmmm d, yyyy", + fullDate: "dddd, mmmm d, yyyy", + shortTime: "h:MM TT", + mediumTime: "h:MM:ss TT", + longTime: "h:MM:ss TT Z", + isoDate: "yyyy-mm-dd", + isoTime: "HH:MM:ss", + isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", + isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" + } + }; + } + + var dF = this.dateFormatCache; + var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, + timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, + timezoneClip = /[^-+\dA-Z]/g, + pad = function (val, len) { + val = String(val); + len = len || 2; + while (val.length < len) val = "0" + val; + return val; + }; + + // You can't provide utc if you skip other args (use the "UTC:" mask prefix) + if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { + mask = date; + date = undefined; + } + + // Passing date through Date applies Date.parse, if necessary + date = date ? new Date(date) : new Date; + if (isNaN(date)) throw SyntaxError("invalid date"); + + mask = String(dF.masks[mask] || mask || dF.masks["default"]); + + // Allow setting the utc argument via the mask + if (mask.slice(0, 4) == "UTC:") { + mask = mask.slice(4); + utc = true; + } + + var _ = utc ? "getUTC" : "get", + d = date[_ + "Date"](), + D = date[_ + "Day"](), + m = date[_ + "Month"](), + y = date[_ + "FullYear"](), + H = date[_ + "Hours"](), + M = date[_ + "Minutes"](), + s = date[_ + "Seconds"](), + L = date[_ + "Milliseconds"](), + o = utc ? 0 : date.getTimezoneOffset(), + flags = { + d: d, + dd: pad(d), + ddd: dF.i18n.dayNames[D], + dddd: dF.i18n.dayNames[D + 7], + m: m + 1, + mm: pad(m + 1), + mmm: dF.i18n.monthNames[m], + mmmm: dF.i18n.monthNames[m + 12], + yy: String(y).slice(2), + yyyy: y, + h: H % 12 || 12, + hh: pad(H % 12 || 12), + H: H, + HH: pad(H), + M: M, + MM: pad(M), + s: s, + ss: pad(s), + l: pad(L, 3), + L: pad(L > 99 ? Math.round(L / 10) : L), + t: H < 12 ? "a" : "p", + tt: H < 12 ? "am" : "pm", + T: H < 12 ? "A" : "P", + TT: H < 12 ? "AM" : "PM", + Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), + o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), + S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] + }; + + return mask.replace(token, function ($0) + { + return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); + }); +}; + +/** + * + */ +Graph.prototype.createLayersDialog = function() +{ + var div = document.createElement('div'); + div.style.position = 'absolute'; + + var model = this.getModel(); + var childCount = model.getChildCount(model.root); + + for (var i = 0; i < childCount; i++) + { + (mxUtils.bind(this, function(layer) + { + var span = document.createElement('div'); + span.style.overflow = 'hidden'; + span.style.textOverflow = 'ellipsis'; + span.style.padding = '2px'; + span.style.whiteSpace = 'nowrap'; + + var cb = document.createElement('input'); + cb.style.display = 'inline-block'; + cb.setAttribute('type', 'checkbox'); + + if (model.isVisible(layer)) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + } + + span.appendChild(cb); + + var title = this.convertValueToString(layer) || (mxResources.get('background') || 'Background'); + span.setAttribute('title', title); + mxUtils.write(span, title); + div.appendChild(span); + + mxEvent.addListener(cb, 'click', function() + { + if (cb.getAttribute('checked') != null) + { + cb.removeAttribute('checked'); + } + else + { + cb.setAttribute('checked', 'checked'); + } + + model.setVisible(layer, cb.checked); + }); + })(model.getChildAt(model.root, i))); + } + + return div; +}; + +/** + * Private helper method. + */ +Graph.prototype.replacePlaceholders = function(cell, str) +{ + var result = []; + + if (str != null) + { + var last = 0; + + while (match = this.placeholderPattern.exec(str)) + { + var val = match[0]; + + if (val.length > 2 && val != '%label%' && val != '%tooltip%') + { + var tmp = null; + + if (match.index > last && str.charAt(match.index - 1) == '%') + { + tmp = val.substring(1); + } + else + { + var name = val.substring(1, val.length - 1); + + // Workaround for invalid char for getting attribute in older versions of IE + if (name.indexOf('{') < 0) + { + var current = cell; + + while (tmp == null && current != null) + { + if (current.value != null && typeof(current.value) == 'object') + { + tmp = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? + current.getAttribute(name) : '') : null; + } + + current = this.model.getParent(current); + } + } + + if (tmp == null) + { + tmp = this.getGlobalVariable(name); + } + } + + result.push(str.substring(last, match.index) + ((tmp != null) ? tmp : val)); + last = match.index + val.length; + } + } + + result.push(str.substring(last)); + } + + return result.join(''); +}; + +/** + * Resolves the given cells in the model and selects them. + */ +Graph.prototype.restoreSelection = function(cells) +{ + if (cells != null && cells.length > 0) + { + var temp = []; + + for (var i = 0; i < cells.length; i++) + { + var newCell = this.model.getCell(cells[i].id); + + if (newCell != null) + { + temp.push(newCell); + } + } + + this.setSelectionCells(temp); + } + else + { + this.clearSelection(); + } +}; + +/** + * Selects cells for connect vertex return value. + */ +Graph.prototype.selectCellsForConnectVertex = function(cells, evt, hoverIcons) +{ + // Selects only target vertex if one exists + if (cells.length == 2 && this.model.isVertex(cells[1])) + { + this.setSelectionCell(cells[1]); + this.scrollCellToVisible(cells[1]); + + if (hoverIcons != null) + { + // Adds hover icons for cloned vertex or hides icons + if (mxEvent.isTouchEvent(evt)) + { + hoverIcons.update(hoverIcons.getState(this.view.getState(cells[1]))); + } + else + { + hoverIcons.reset(); + } + } + } + else + { + this.setSelectionCells(cells); + } +}; + +/** + * Adds a connection to the given vertex. + */ +Graph.prototype.connectVertex = function(source, direction, length, evt, forceClone, ignoreCellAt) +{ + // Ignores relative edge labels + if (source.geometry.relative && this.model.isEdge(source.parent)) + { + return []; + } + + ignoreCellAt = (ignoreCellAt) ? ignoreCellAt : false; + + var pt = (source.geometry.relative && source.parent.geometry != null) ? + new mxPoint(source.parent.geometry.width * source.geometry.x, source.parent.geometry.height * source.geometry.y) : + new mxPoint(source.geometry.x, source.geometry.y); + + if (direction == mxConstants.DIRECTION_NORTH) + { + pt.x += source.geometry.width / 2; + pt.y -= length ; + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + pt.x += source.geometry.width / 2; + pt.y += source.geometry.height + length; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + pt.x -= length; + pt.y += source.geometry.height / 2; + } + else + { + pt.x += source.geometry.width + length; + pt.y += source.geometry.height / 2; + } + + var parentState = this.view.getState(this.model.getParent(source)); + var s = this.view.scale; + var t = this.view.translate; + var dx = t.x * s; + var dy = t.y * s; + + if (parentState != null && this.model.isVertex(parentState.cell)) + { + dx = parentState.x; + dy = parentState.y; + } + + // Workaround for relative child cells + if (this.model.isVertex(source.parent) && source.geometry.relative) + { + pt.x += source.parent.geometry.x; + pt.y += source.parent.geometry.y; + } + + // Checks actual end point of edge for target cell + var target = (ignoreCellAt || (mxEvent.isControlDown(evt) && !forceClone)) ? + null : this.getCellAt(dx + pt.x * s, dy + pt.y * s); + + if (this.model.isAncestor(target, source)) + { + target = null; + } + + // Checks if target or ancestor is locked + var temp = target; + + while (temp != null) + { + if (this.isCellLocked(temp)) + { + target = null; + break; + } + + temp = this.model.getParent(temp); + } + + // Checks if source and target intersect + if (target != null) + { + var sourceState = this.view.getState(source); + var targetState = this.view.getState(target); + + if (sourceState != null && targetState != null && mxUtils.intersects(sourceState, targetState)) + { + target = null; + } + } + + var duplicate = !mxEvent.isShiftDown(evt) || forceClone; + + if (duplicate) + { + if (direction == mxConstants.DIRECTION_NORTH) + { + pt.y -= source.geometry.height / 2; + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + pt.y += source.geometry.height / 2; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + pt.x -= source.geometry.width / 2; + } + else + { + pt.x += source.geometry.width / 2; + } + } + + // Uses connectable parent vertex if one exists + if (target != null && !this.isCellConnectable(target)) + { + var parent = this.getModel().getParent(target); + + if (this.getModel().isVertex(parent) && this.isCellConnectable(parent)) + { + target = parent; + } + } + + if (target == source || this.model.isEdge(target) || !this.isCellConnectable(target)) + { + target = null; + } + + var result = []; + + this.model.beginUpdate(); + try + { + var swimlane = target != null && this.isSwimlane(target); + var realTarget = (!swimlane) ? target : null; + + if (realTarget == null && duplicate) + { + // Handles relative children + var cellToClone = source; + var geo = this.getCellGeometry(source); + + while (geo != null && geo.relative) + { + cellToClone = this.getModel().getParent(cellToClone); + geo = this.getCellGeometry(cellToClone); + } + + // Handle consistuents for cloning + cellToClone = this.getCompositeParent(cellToClone); + realTarget = this.duplicateCells([cellToClone], false)[0]; + + var geo = this.getCellGeometry(realTarget); + + if (geo != null) + { + geo.x = pt.x - geo.width / 2; + geo.y = pt.y - geo.height / 2; + } + + if (swimlane) + { + this.addCells([realTarget], target, null, null, null, true); + target = null; + } + } + + // Never connects children in stack layouts + var layout = null; + + if (this.layoutManager != null) + { + layout = this.layoutManager.getLayout(this.model.getParent(source)); + } + + var edge = ((mxEvent.isControlDown(evt) && duplicate) || (target == null && layout != null && layout.constructor == mxStackLayout)) ? null : + this.insertEdge(this.model.getParent(source), null, '', source, realTarget, this.createCurrentEdgeStyle()); + + // Inserts edge before source + if (edge != null && this.connectionHandler.insertBeforeSource) + { + var index = null; + var tmp = source; + + while (tmp.parent != null && tmp.geometry != null && + tmp.geometry.relative && tmp.parent != edge.parent) + { + tmp = this.model.getParent(tmp); + } + + if (tmp != null && tmp.parent != null && tmp.parent == edge.parent) + { + var index = tmp.parent.getIndex(tmp); + this.model.add(tmp.parent, edge, index); + } + } + + // Special case: Click on west icon puts clone before cell + if (target == null && realTarget != null && layout != null && source.parent != null && + layout.constructor == mxStackLayout && direction == mxConstants.DIRECTION_WEST) + { + var index = source.parent.getIndex(source); + this.model.add(source.parent, realTarget, index); + } + + if (edge != null) + { + result.push(edge); + } + + if (target == null && realTarget != null) + { + result.push(realTarget); + } + + if (realTarget == null && edge != null) + { + edge.geometry.setTerminalPoint(pt, false); + } + + if (edge != null) + { + this.fireEvent(new mxEventObject('cellsInserted', 'cells', [edge])); + } + } + finally + { + this.model.endUpdate(); + } + + return result; +}; + +/** + * Returns all labels in the diagram as a string. + */ +Graph.prototype.getIndexableText = function() +{ + var tmp = document.createElement('div'); + var labels = []; + var label = ''; + + for (var key in this.model.cells) + { + var cell = this.model.cells[key]; + + if (this.model.isVertex(cell) || this.model.isEdge(cell)) + { + if (this.isHtmlLabel(cell)) + { + tmp.innerHTML = this.getLabel(cell); + label = mxUtils.extractTextWithWhitespace([tmp]); + } + else + { + label = this.getLabel(cell); + } + + label = mxUtils.trim(label.replace(/[\x00-\x1F\x7F-\x9F]|\s+/g, ' ')); + + if (label.length > 0) + { + labels.push(label); + } + } + } + + return labels.join(' '); +}; + +/** + * Returns the label for the given cell. + */ +Graph.prototype.convertValueToString = function(cell) +{ + var value = this.model.getValue(cell); + + if (value != null && typeof(value) == 'object') + { + if (this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') != null) + { + var name = cell.getAttribute('placeholder'); + var current = cell; + var result = null; + + while (result == null && current != null) + { + if (current.value != null && typeof(current.value) == 'object') + { + result = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? + current.getAttribute(name) : '') : null; + } + + current = this.model.getParent(current); + } + + return result || ''; + } + else + { + return value.getAttribute('label') || ''; + } + } + + return mxGraph.prototype.convertValueToString.apply(this, arguments); +}; + +/** + * Returns the link for the given cell. + */ +Graph.prototype.getLinksForState = function(state) +{ + if (state != null && state.text != null && state.text.node != null) + { + return state.text.node.getElementsByTagName('a'); + } + + return null; +}; + +/** + * Returns the link for the given cell. + */ +Graph.prototype.getLinkForCell = function(cell) +{ + if (cell.value != null && typeof(cell.value) == 'object') + { + var link = cell.value.getAttribute('link'); + + // Removes links with leading javascript: protocol + // TODO: Check more possible attack vectors + if (link != null && link.toLowerCase().substring(0, 11) === 'javascript:') + { + link = link.substring(11); + } + + return link; + } + + return null; +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.getCellStyle = function(cell) +{ + var style = mxGraph.prototype.getCellStyle.apply(this, arguments); + + if (cell != null && this.layoutManager != null) + { + var parent = this.model.getParent(cell); + + if (this.model.isVertex(parent) && this.isCellCollapsed(cell)) + { + var layout = this.layoutManager.getLayout(parent); + + if (layout != null && layout.constructor == mxStackLayout) + { + style[mxConstants.STYLE_HORIZONTAL] = !layout.horizontal; + } + } + } + + return style; +}; + +/** + * Disables alternate width persistence for stack layout parents + */ +Graph.prototype.updateAlternateBounds = function(cell, geo, willCollapse) +{ + if (cell != null && geo != null && this.layoutManager != null && geo.alternateBounds != null) + { + var layout = this.layoutManager.getLayout(this.model.getParent(cell)); + + if (layout != null && layout.constructor == mxStackLayout) + { + if (layout.horizontal) + { + geo.alternateBounds.height = 0; + } + else + { + geo.alternateBounds.width = 0; + } + } + } + + mxGraph.prototype.updateAlternateBounds.apply(this, arguments); +}; + +/** + * Adds Shift+collapse/expand and size management for folding inside stack + */ +Graph.prototype.isMoveCellsEvent = function(evt, state) +{ + return mxEvent.isShiftDown(evt) || mxUtils.getValue(state.style, 'moveCells', '0') == '1'; +}; + +/** + * Adds Shift+collapse/expand and size management for folding inside stack + */ +Graph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt) +{ + recurse = (recurse != null) ? recurse : false; + + if (cells == null) + { + cells = this.getFoldableCells(this.getSelectionCells(), collapse); + } + + if (cells != null) + { + this.model.beginUpdate(); + + try + { + mxGraph.prototype.foldCells.apply(this, arguments); + + // Resizes all parent stacks if alt is not pressed + if (this.layoutManager != null) + { + for (var i = 0; i < cells.length; i++) + { + var state = this.view.getState(cells[i]); + var geo = this.getCellGeometry(cells[i]); + + if (state != null && geo != null) + { + var dx = Math.round(geo.width - state.width / this.view.scale); + var dy = Math.round(geo.height - state.height / this.view.scale); + + if (dy != 0 || dx != 0) + { + var parent = this.model.getParent(cells[i]); + var layout = this.layoutManager.getLayout(parent); + + if (layout == null) + { + // Moves cells to the right and down after collapse/expand + if (evt != null && this.isMoveCellsEvent(evt, state)) + { + this.moveSiblings(state, parent, dx, dy); + } + } + else if ((evt == null || !mxEvent.isAltDown(evt)) && + layout.constructor == mxStackLayout && !layout.resizeLast) + { + this.resizeParentStacks(parent, layout, dx, dy); + } + } + } + } + } + } + finally + { + this.model.endUpdate(); + } + + // Selects cells after folding + if (this.isEnabled()) + { + this.setSelectionCells(cells); + } + } +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.moveSiblings = function(state, parent, dx, dy) +{ + this.model.beginUpdate(); + try + { + var cells = this.getCellsBeyond(state.x, state.y, parent, true, true); + + for (var i = 0; i < cells.length; i++) + { + if (cells[i] != state.cell) + { + var tmp = this.view.getState(cells[i]); + var geo = this.getCellGeometry(cells[i]); + + if (tmp != null && geo != null) + { + geo = geo.clone(); + geo.translate(Math.round(dx * Math.max(0, Math.min(1, (tmp.x - state.x) / state.width))), + Math.round(dy * Math.max(0, Math.min(1, (tmp.y - state.y) / state.height)))); + this.model.setGeometry(cells[i], geo); + } + } + } + } + finally + { + this.model.endUpdate(); + } +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.resizeParentStacks = function(parent, layout, dx, dy) +{ + if (this.layoutManager != null && layout != null && layout.constructor == mxStackLayout && !layout.resizeLast) + { + this.model.beginUpdate(); + try + { + var dir = layout.horizontal; + + // Bubble resize up for all parent stack layouts with same orientation + while (parent != null && layout != null && layout.constructor == mxStackLayout && + layout.horizontal == dir && !layout.resizeLast) + { + var pgeo = this.getCellGeometry(parent); + var pstate = this.view.getState(parent); + + if (pstate != null && pgeo != null) + { + pgeo = pgeo.clone(); + + if (layout.horizontal) + { + pgeo.width += dx + Math.min(0, pstate.width / this.view.scale - pgeo.width); + } + else + { + pgeo.height += dy + Math.min(0, pstate.height / this.view.scale - pgeo.height); + } + + this.model.setGeometry(parent, pgeo); + } + + parent = this.model.getParent(parent); + layout = this.layoutManager.getLayout(parent); + } + } + finally + { + this.model.endUpdate(); + } + } +}; + +/** + * Disables drill-down for non-swimlanes. + */ +Graph.prototype.isContainer = function(cell) +{ + var style = this.getCurrentCellStyle(cell); + + if (this.isSwimlane(cell)) + { + return style['container'] != '0'; + } + else + { + return style['container'] == '1'; + } +}; + +/** + * Adds a expand style. + */ +Graph.prototype.isExtendParent = function(cell) +{ + var parent = this.model.getParent(cell); + + if (parent != null) + { + var style = this.getCurrentCellStyle(parent); + + if (style['expand'] != null) + { + return style['expand'] != '0'; + } + } + + return mxGraph.prototype.isExtendParent.apply(this, arguments); +}; + +/** + * Adds a connectable style. + */ +Graph.prototype.isCellConnectable = function(cell) +{ + var style = this.getCurrentCellStyle(cell); + + return (style['connectable'] != null) ? style['connectable'] != '0' : + mxGraph.prototype.isCellConnectable.apply(this, arguments); +}; + +/** + * Adds labelMovable style. + */ +Graph.prototype.isLabelMovable = function(cell) +{ + var style = this.getCurrentCellStyle(cell); + + return (style['movableLabel'] != null) ? style['movableLabel'] != '0' : + mxGraph.prototype.isLabelMovable.apply(this, arguments); +}; + +/** + * Function: selectAll + * + * Selects all children of the given parent cell or the children of the + * default parent if no parent is specified. To select leaf vertices and/or + * edges use . + * + * Parameters: + * + * parent - Optional whose children should be selected. + * Default is . + */ +Graph.prototype.selectAll = function(parent) +{ + parent = parent || this.getDefaultParent(); + + if (!this.isCellLocked(parent)) + { + mxGraph.prototype.selectAll.apply(this, arguments); + } +}; + +/** + * Function: selectCells + * + * Selects all vertices and/or edges depending on the given boolean + * arguments recursively, starting at the given parent or the default + * parent if no parent is specified. Use to select all cells. + * For vertices, only cells with no children are selected. + * + * Parameters: + * + * vertices - Boolean indicating if vertices should be selected. + * edges - Boolean indicating if edges should be selected. + * parent - Optional that acts as the root of the recursion. + * Default is . + */ +Graph.prototype.selectCells = function(vertices, edges, parent) +{ + parent = parent || this.getDefaultParent(); + + if (!this.isCellLocked(parent)) + { + mxGraph.prototype.selectCells.apply(this, arguments); + } +}; + +/** + * Function: getSwimlaneAt + * + * Returns the bottom-most swimlane that intersects the given point (x, y) + * in the cell hierarchy that starts at the given parent. + * + * Parameters: + * + * x - X-coordinate of the location to be checked. + * y - Y-coordinate of the location to be checked. + * parent - that should be used as the root of the recursion. + * Default is . + */ +Graph.prototype.getSwimlaneAt = function (x, y, parent) +{ + var result = mxGraph.prototype.getSwimlaneAt.apply(this, arguments); + + if (this.isCellLocked(result)) + { + result = null; + } + + return result; +}; + +/** + * Disables folding for non-swimlanes. + */ +Graph.prototype.isCellFoldable = function(cell) +{ + var style = this.getCurrentCellStyle(cell); + + return this.foldingEnabled && (style['treeFolding'] == '1' || + (!this.isCellLocked(cell) && + ((this.isContainer(cell) && style['collapsible'] != '0') || + (!this.isContainer(cell) && style['collapsible'] == '1')))); +}; + +/** + * Stops all interactions and clears the selection. + */ +Graph.prototype.reset = function() +{ + if (this.isEditing()) + { + this.stopEditing(true); + } + + this.escape(); + + if (!this.isSelectionEmpty()) + { + this.clearSelection(); + } +}; + +/** + * Overridden to limit zoom to 1% - 16.000%. + */ +Graph.prototype.zoom = function(factor, center) +{ + factor = Math.max(0.01, Math.min(this.view.scale * factor, 160)) / this.view.scale; + + mxGraph.prototype.zoom.apply(this, arguments); +}; + +/** + * Function: zoomIn + * + * Zooms into the graph by . + */ +Graph.prototype.zoomIn = function() +{ + // Switches to 1% zoom steps below 15% + if (this.view.scale < 0.15) + { + this.zoom((this.view.scale + 0.01) / this.view.scale); + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.zoom((Math.round(this.view.scale * this.zoomFactor * 20) / 20) / this.view.scale); + } +}; + +/** + * Function: zoomOut + * + * Zooms out of the graph by . + */ +Graph.prototype.zoomOut = function() +{ + // Switches to 1% zoom steps below 15% + if (this.view.scale <= 0.15) + { + this.zoom((this.view.scale - 0.01) / this.view.scale); + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.zoom((Math.round(this.view.scale * (1 / this.zoomFactor) * 20) / 20) / this.view.scale); + } +}; + +/** + * Overrides tooltips to show custom tooltip or metadata. + */ +Graph.prototype.getTooltipForCell = function(cell) +{ + var tip = ''; + + if (mxUtils.isNode(cell.value)) + { + var tmp = cell.value.getAttribute('tooltip'); + + if (tmp != null) + { + if (tmp != null && this.isReplacePlaceholders(cell)) + { + tmp = this.replacePlaceholders(cell, tmp); + } + + tip = this.sanitizeHtml(tmp); + } + else + { + var ignored = this.builtInProperties; + var attrs = cell.value.attributes; + var temp = []; + + // Hides links in edit mode + if (this.isEnabled()) + { + ignored.push('link'); + } + + for (var i = 0; i < attrs.length; i++) + { + if (mxUtils.indexOf(ignored, attrs[i].nodeName) < 0 && attrs[i].nodeValue.length > 0) + { + temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); + } + } + + // Sorts by name + temp.sort(function(a, b) + { + if (a.name < b.name) + { + return -1; + } + else if (a.name > b.name) + { + return 1; + } + else + { + return 0; + } + }); + + for (var i = 0; i < temp.length; i++) + { + if (temp[i].name != 'link' || !this.isCustomLink(temp[i].value)) + { + tip += ((temp[i].name != 'link') ? '' + temp[i].name + ': ' : '') + + mxUtils.htmlEntities(temp[i].value) + '\n'; + } + } + + if (tip.length > 0) + { + tip = tip.substring(0, tip.length - 1); + + if (mxClient.IS_SVG) + { + tip = '
' + tip + '
'; + } + } + } + } + + return tip; +}; + +/** + * Turns the given string into an array. + */ +Graph.prototype.stringToBytes = function(str) +{ + return Graph.stringToBytes(str); +}; + +/** + * Turns the given array into a string. + */ +Graph.prototype.bytesToString = function(arr) +{ + return Graph.bytesToString(arr); +}; + +/** + * Returns a base64 encoded version of the compressed outer XML of the given node. + */ +Graph.prototype.compressNode = function(node) +{ + return Graph.compressNode(node); +}; + +/** + * Returns a base64 encoded version of the compressed string. + */ +Graph.prototype.compress = function(data, deflate) +{ + return Graph.compress(data, deflate); +}; + +/** + * Returns a decompressed version of the base64 encoded string. + */ +Graph.prototype.decompress = function(data, inflate) +{ + return Graph.decompress(data, inflate); +}; + +/** + * Redirects to Graph.zapGremlins. + */ +Graph.prototype.zapGremlins = function(text) +{ + return Graph.zapGremlins(text); +}; + +/** + * Hover icons are used for hover, vertex handler and drag from sidebar. + */ +HoverIcons = function(graph) +{ + this.graph = graph; + this.init(); +}; + +/** + * Up arrow. + */ +HoverIcons.prototype.arrowSpacing = 2; + +/** + * Delay to switch to another state for overlapping bbox. Default is 500ms. + */ +HoverIcons.prototype.updateDelay = 500; + +/** + * Delay to switch between states. Default is 140ms. + */ +HoverIcons.prototype.activationDelay = 140; + +/** + * Up arrow. + */ +HoverIcons.prototype.currentState = null; + +/** + * Up arrow. + */ +HoverIcons.prototype.activeArrow = null; + +/** + * Up arrow. + */ +HoverIcons.prototype.inactiveOpacity = 15; + +/** + * Up arrow. + */ +HoverIcons.prototype.cssCursor = 'copy'; + +/** + * Whether to hide arrows that collide with vertices. + * LATER: Add keyboard override, touch support. + */ +HoverIcons.prototype.checkCollisions = true; + +/** + * Up arrow. + */ +HoverIcons.prototype.arrowFill = '#29b6f2'; + +/** + * Up arrow. + */ +HoverIcons.prototype.triangleUp = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-up.png', 26, 14) : + Graph.createSvgImage(18, 28, ''); + +/** + * Right arrow. + */ +HoverIcons.prototype.triangleRight = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-right.png', 14, 26) : + Graph.createSvgImage(26, 18, ''); + +/** + * Down arrow. + */ +HoverIcons.prototype.triangleDown = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-down.png', 26, 14) : + Graph.createSvgImage(18, 26, ''); + +/** + * Left arrow. + */ +HoverIcons.prototype.triangleLeft = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-left.png', 14, 26) : + Graph.createSvgImage(28, 18, ''); + +/** + * Round target. + */ +HoverIcons.prototype.roundDrop = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/round-drop.png', 26, 26) : + Graph.createSvgImage(26, 26, ''); + +/** + * Refresh target. + */ +HoverIcons.prototype.refreshTarget = new mxImage((mxClient.IS_SVG) ? 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjM2cHgiIGhlaWdodD0iMzZweCI+PGVsbGlwc2UgZmlsbD0iIzI5YjZmMiIgY3g9IjEyIiBjeT0iMTIiIHJ4PSIxMiIgcnk9IjEyIi8+PHBhdGggdHJhbnNmb3JtPSJzY2FsZSgwLjgpIHRyYW5zbGF0ZSgyLjQsIDIuNCkiIHN0cm9rZT0iI2ZmZiIgZmlsbD0iI2ZmZiIgZD0iTTEyIDZ2M2w0LTQtNC00djNjLTQuNDIgMC04IDMuNTgtOCA4IDAgMS41Ny40NiAzLjAzIDEuMjQgNC4yNkw2LjcgMTQuOGMtLjQ1LS44My0uNy0xLjc5LS43LTIuOCAwLTMuMzEgMi42OS02IDYtNnptNi43NiAxLjc0TDE3LjMgOS4yYy40NC44NC43IDEuNzkuNyAyLjggMCAzLjMxLTIuNjkgNi02IDZ2LTNsLTQgNCA0IDR2LTNjNC40MiAwIDgtMy41OCA4LTggMC0xLjU3LS40Ni0zLjAzLTEuMjQtNC4yNnoiLz48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+Cg==' : + IMAGE_PATH + '/refresh.png', 38, 38); + +/** + * Tolerance for hover icon clicks. + */ +HoverIcons.prototype.tolerance = (mxClient.IS_TOUCH) ? 6 : 0; + +/** + * + */ +HoverIcons.prototype.init = function() +{ + this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip')); + this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip')); + this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip')); + this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip')); + + this.elts = [this.arrowUp, this.arrowRight, this.arrowDown, this.arrowLeft]; + + this.resetHandler = mxUtils.bind(this, function() + { + this.reset(); + }); + + this.repaintHandler = mxUtils.bind(this, function() + { + this.repaint(); + }); + + this.graph.selectionModel.addListener(mxEvent.CHANGE, this.resetHandler); + this.graph.model.addListener(mxEvent.CHANGE, this.repaintHandler); + this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler); + this.graph.view.addListener(mxEvent.TRANSLATE, this.repaintHandler); + this.graph.view.addListener(mxEvent.SCALE, this.repaintHandler); + this.graph.view.addListener(mxEvent.DOWN, this.repaintHandler); + this.graph.view.addListener(mxEvent.UP, this.repaintHandler); + this.graph.addListener(mxEvent.ROOT, this.repaintHandler); + this.graph.addListener(mxEvent.ESCAPE, this.resetHandler); + mxEvent.addListener(this.graph.container, 'scroll', this.resetHandler); + + // Resets the mouse point on escape + this.graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function() + { + this.mouseDownPoint = null; + })); + + // Removes hover icons if mouse leaves the container + mxEvent.addListener(this.graph.container, 'mouseleave', mxUtils.bind(this, function(evt) + { + // Workaround for IE11 firing mouseleave for touch in diagram + if (evt.relatedTarget != null && mxEvent.getSource(evt) == this.graph.container) + { + this.setDisplay('none'); + } + })); + + // Resets current state when in-place editor starts + this.graph.addListener(mxEvent.START_EDITING, mxUtils.bind(this, function(evt) + { + this.reset(); + })); + + // Resets current state after update of selection state for touch events + var graphClick = this.graph.click; + this.graph.click = mxUtils.bind(this, function(me) + { + graphClick.apply(this.graph, arguments); + + if (this.currentState != null && !this.graph.isCellSelected(this.currentState.cell) && + mxEvent.isTouchEvent(me.getEvent()) && !this.graph.model.isVertex(me.getCell())) + { + this.reset(); + } + }); + + // Checks if connection handler was active in mouse move + // as workaround for possible double connection inserted + var connectionHandlerActive = false; + + // Implements a listener for hover and click handling + this.graph.addMouseListener( + { + mouseDown: mxUtils.bind(this, function(sender, me) + { + connectionHandlerActive = false; + var evt = me.getEvent(); + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (!this.isActive()) + { + var state = this.getState(me.getState()); + + if (state != null || !mxEvent.isTouchEvent(evt)) + { + this.update(state); + } + } + + this.setDisplay('none'); + }), + mouseMove: mxUtils.bind(this, function(sender, me) + { + var evt = me.getEvent(); + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (!this.graph.isMouseDown && !mxEvent.isTouchEvent(evt)) + { + this.update(this.getState(me.getState()), + me.getGraphX(), me.getGraphY()); + } + + if (this.graph.connectionHandler != null && + this.graph.connectionHandler.shape != null) + { + connectionHandlerActive = true; + } + }), + mouseUp: mxUtils.bind(this, function(sender, me) + { + var evt = me.getEvent(); + var pt = mxUtils.convertPoint(this.graph.container, + mxEvent.getClientX(evt), mxEvent.getClientY(evt)) + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (this.isActive() && !connectionHandlerActive && + this.mouseDownPoint != null) + { + this.click(this.currentState, this.getDirection(), me); + } + else if (this.isActive()) + { + // Selects target vertex after drag and clone if not only new edge was inserted + if (this.graph.getSelectionCount() != 1 || !this.graph.model.isEdge( + this.graph.getSelectionCell())) + { + this.update(this.getState(this.graph.view.getState( + this.graph.getCellAt(me.getGraphX(), me.getGraphY())))); + } + else + { + this.reset(); + } + } + else if (mxEvent.isTouchEvent(evt) || (this.bbox != null && + mxUtils.contains(this.bbox, me.getGraphX(), me.getGraphY()))) + { + // Shows existing hover icons if inside bounding box + this.setDisplay(''); + this.repaint(); + } + else if (!mxEvent.isTouchEvent(evt)) + { + this.reset(); + } + + connectionHandlerActive = false; + this.resetActiveArrow(); + }) + }); +}; + +/** + * + */ +HoverIcons.prototype.isResetEvent = function(evt, allowShift) +{ + return mxEvent.isAltDown(evt) || (this.activeArrow == null && mxEvent.isShiftDown(evt)) || + mxEvent.isMetaDown(evt) || (mxEvent.isPopupTrigger(evt) && !mxEvent.isControlDown(evt)); +}; + +/** + * + */ +HoverIcons.prototype.createArrow = function(img, tooltip) +{ + var arrow = null; + + if (mxClient.IS_IE && !mxClient.IS_SVG) + { + // Workaround for PNG images in IE6 + if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') + { + arrow = document.createElement(mxClient.VML_PREFIX + ':image'); + arrow.setAttribute('src', img.src); + arrow.style.borderStyle = 'none'; + } + else + { + arrow = document.createElement('div'); + arrow.style.backgroundImage = 'url(' + img.src + ')'; + arrow.style.backgroundPosition = 'center'; + arrow.style.backgroundRepeat = 'no-repeat'; + } + + arrow.style.width = (img.width + 4) + 'px'; + arrow.style.height = (img.height + 4) + 'px'; + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + } + else + { + arrow = mxUtils.createImage(img.src); + arrow.style.width = img.width + 'px'; + arrow.style.height = img.height + 'px'; + arrow.style.padding = this.tolerance + 'px'; + } + + if (tooltip != null) + { + arrow.setAttribute('title', tooltip); + } + + arrow.style.position = 'absolute'; + arrow.style.cursor = this.cssCursor; + + mxEvent.addGestureListeners(arrow, mxUtils.bind(this, function(evt) + { + if (this.currentState != null && !this.isResetEvent(evt)) + { + this.mouseDownPoint = mxUtils.convertPoint(this.graph.container, + mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + this.drag(evt, this.mouseDownPoint.x, this.mouseDownPoint.y); + this.activeArrow = arrow; + this.setDisplay('none'); + mxEvent.consume(evt); + } + })); + + // Captures mouse events as events on graph + mxEvent.redirectMouseEvents(arrow, this.graph, this.currentState); + + mxEvent.addListener(arrow, 'mouseenter', mxUtils.bind(this, function(evt) + { + // Workaround for Firefox firing mouseenter on touchend + if (mxEvent.isMouseEvent(evt)) + { + if (this.activeArrow != null && this.activeArrow != arrow) + { + mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); + } + + this.graph.connectionHandler.constraintHandler.reset(); + mxUtils.setOpacity(arrow, 100); + this.activeArrow = arrow; + } + })); + + mxEvent.addListener(arrow, 'mouseleave', mxUtils.bind(this, function(evt) + { + // Workaround for IE11 firing this event on touch + if (!this.graph.isMouseDown) + { + this.resetActiveArrow(); + } + })); + + return arrow; +}; + +/** + * + */ +HoverIcons.prototype.resetActiveArrow = function() +{ + if (this.activeArrow != null) + { + mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); + this.activeArrow = null; + } +}; + +/** + * + */ +HoverIcons.prototype.getDirection = function() +{ + var dir = mxConstants.DIRECTION_EAST; + + if (this.activeArrow == this.arrowUp) + { + dir = mxConstants.DIRECTION_NORTH; + } + else if (this.activeArrow == this.arrowDown) + { + dir = mxConstants.DIRECTION_SOUTH; + } + else if (this.activeArrow == this.arrowLeft) + { + dir = mxConstants.DIRECTION_WEST; + } + + return dir; +}; + +/** + * + */ +HoverIcons.prototype.visitNodes = function(visitor) +{ + for (var i = 0; i < this.elts.length; i++) + { + if (this.elts[i] != null) + { + visitor(this.elts[i]); + } + } +}; + +/** + * + */ +HoverIcons.prototype.removeNodes = function() +{ + this.visitNodes(function(elt) + { + if (elt.parentNode != null) + { + elt.parentNode.removeChild(elt); + } + }); +}; + +/** + * + */ +HoverIcons.prototype.setDisplay = function(display) +{ + this.visitNodes(function(elt) + { + elt.style.display = display; + }); +}; + +/** + * + */ +HoverIcons.prototype.isActive = function() +{ + return this.activeArrow != null && this.currentState != null; +}; + +/** + * + */ +HoverIcons.prototype.drag = function(evt, x, y) +{ + this.graph.popupMenuHandler.hideMenu(); + this.graph.stopEditing(false); + + // Checks if state was removed in call to stopEditing above + if (this.currentState != null) + { + this.graph.connectionHandler.start(this.currentState, x, y); + this.graph.isMouseTrigger = mxEvent.isMouseEvent(evt); + this.graph.isMouseDown = true; + + // Hides handles for selection cell + var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); + + if (handler != null) + { + handler.setHandlesVisible(false); + } + + // Ctrl+shift drag sets source constraint + var es = this.graph.connectionHandler.edgeState; + + if (evt != null && mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt) && es != null && + mxUtils.getValue(es.style, mxConstants.STYLE_EDGE, null) === 'orthogonalEdgeStyle') + { + var direction = this.getDirection(); + es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction); + es.style['sourcePortConstraint'] = direction; + } + } +}; + +/** + * + */ +HoverIcons.prototype.getStateAt = function(state, x, y) +{ + return this.graph.view.getState(this.graph.getCellAt(x, y)); +}; + +/** + * + */ +HoverIcons.prototype.click = function(state, dir, me) +{ + var evt = me.getEvent(); + var x = me.getGraphX(); + var y = me.getGraphY(); + + var tmp = this.getStateAt(state, x, y); + + if (tmp != null && this.graph.model.isEdge(tmp.cell) && !mxEvent.isControlDown(evt) && + (tmp.getVisibleTerminalState(true) == state || tmp.getVisibleTerminalState(false) == state)) + { + this.graph.setSelectionCell(tmp.cell); + this.reset(); + } + else if (state != null) + { + this.graph.selectCellsForConnectVertex(this.graph.connectVertex( + state.cell, dir, this.graph.defaultEdgeLength, evt), evt, this); + } + + me.consume(); +}; + +/** + * + */ +HoverIcons.prototype.reset = function(clearTimeout) +{ + clearTimeout = (clearTimeout == null) ? true : clearTimeout; + + if (clearTimeout && this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + this.mouseDownPoint = null; + this.currentState = null; + this.activeArrow = null; + this.removeNodes(); + this.bbox = null; +}; + + + +/** + * + */ +HoverIcons.prototype.repaint = function() +{ + this.bbox = null; + + if (this.currentState != null) + { + // Checks if cell was deleted + this.currentState = this.getState(this.currentState); + + // Cell was deleted + if (this.currentState != null && + this.graph.model.isVertex(this.currentState.cell) && + this.graph.isCellConnectable(this.currentState.cell)) + { + var bds = mxRectangle.fromRectangle(this.currentState); + + // Uses outer bounding box to take rotation into account + if (this.currentState.shape != null && this.currentState.shape.boundingBox != null) + { + bds = mxRectangle.fromRectangle(this.currentState.shape.boundingBox); + } + + bds.grow(this.graph.tolerance); + bds.grow(this.arrowSpacing); + + var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); + var rotationBbox = null; + + if (handler != null) + { + bds.x -= handler.horizontalOffset / 2; + bds.y -= handler.verticalOffset / 2; + bds.width += handler.horizontalOffset; + bds.height += handler.verticalOffset; + + // Adds bounding box of rotation handle to avoid overlap + if (handler.rotationShape != null && handler.rotationShape.node != null && + handler.rotationShape.node.style.visibility != 'hidden' && + handler.rotationShape.node.style.display != 'none' && + handler.rotationShape.boundingBox != null) + { + rotationBbox = handler.rotationShape.boundingBox; + } + } + + // Positions arrows avoid collisions with rotation handle + var positionArrow = mxUtils.bind(this, function(arrow, x, y) + { + if (rotationBbox != null) + { + var bbox = new mxRectangle(x, y, arrow.clientWidth, arrow.clientHeight); + + if (mxUtils.intersects(bbox, rotationBbox)) + { + if (arrow == this.arrowUp) + { + y -= bbox.y + bbox.height - rotationBbox.y; + } + else if (arrow == this.arrowRight) + { + x += rotationBbox.x + rotationBbox.width - bbox.x; + } + else if (arrow == this.arrowDown) + { + y += rotationBbox.y + rotationBbox.height - bbox.y; + } + else if (arrow == this.arrowLeft) + { + x -= bbox.x + bbox.width - rotationBbox.x; + } + } + } + + arrow.style.left = x + 'px'; + arrow.style.top = y + 'px'; + mxUtils.setOpacity(arrow, this.inactiveOpacity); + }); + + positionArrow(this.arrowUp, + Math.round(this.currentState.getCenterX() - this.triangleUp.width / 2 - this.tolerance), + Math.round(bds.y - this.triangleUp.height - this.tolerance)); + + positionArrow(this.arrowRight, Math.round(bds.x + bds.width - this.tolerance), + Math.round(this.currentState.getCenterY() - this.triangleRight.height / 2 - this.tolerance)); + + positionArrow(this.arrowDown, parseInt(this.arrowUp.style.left), + Math.round(bds.y + bds.height - this.tolerance)); + + positionArrow(this.arrowLeft, Math.round(bds.x - this.triangleLeft.width - this.tolerance), + parseInt(this.arrowRight.style.top)); + + if (this.checkCollisions) + { + var right = this.graph.getCellAt(bds.x + bds.width + + this.triangleRight.width / 2, this.currentState.getCenterY()); + var left = this.graph.getCellAt(bds.x - this.triangleLeft.width / 2, this.currentState.getCenterY()); + var top = this.graph.getCellAt(this.currentState.getCenterX(), bds.y - this.triangleUp.height / 2); + var bottom = this.graph.getCellAt(this.currentState.getCenterX(), bds.y + bds.height + this.triangleDown.height / 2); + + // Shows hover icons large cell is behind all directions of current cell + if (right != null && right == left && left == top && top == bottom) + { + right = null; + left = null; + top = null; + bottom = null; + } + + var currentGeo = this.graph.getCellGeometry(this.currentState.cell); + + var checkCollision = mxUtils.bind(this, function(cell, arrow) + { + var geo = this.graph.model.isVertex(cell) && this.graph.getCellGeometry(cell); + + // Ignores collision if vertex is more than 3 times the size of this vertex + if (cell != null && !this.graph.model.isAncestor(cell, this.currentState.cell) && + !this.graph.isSwimlane(cell) && (geo == null || currentGeo == null || + (geo.height < 3 * currentGeo.height && geo.width < 3 * currentGeo.width))) + { + arrow.style.visibility = 'hidden'; + } + else + { + arrow.style.visibility = 'visible'; + } + }); + + checkCollision(right, this.arrowRight); + checkCollision(left, this.arrowLeft); + checkCollision(top, this.arrowUp); + checkCollision(bottom, this.arrowDown); + } + else + { + this.arrowLeft.style.visibility = 'visible'; + this.arrowRight.style.visibility = 'visible'; + this.arrowUp.style.visibility = 'visible'; + this.arrowDown.style.visibility = 'visible'; + } + + if (this.graph.tooltipHandler.isEnabled()) + { + this.arrowLeft.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowRight.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowUp.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowDown.setAttribute('title', mxResources.get('plusTooltip')); + } + else + { + this.arrowLeft.removeAttribute('title'); + this.arrowRight.removeAttribute('title'); + this.arrowUp.removeAttribute('title'); + this.arrowDown.removeAttribute('title'); + } + } + else + { + this.reset(); + } + + // Updates bounding box + if (this.currentState != null) + { + this.bbox = this.computeBoundingBox(); + + // Adds tolerance for hover + if (this.bbox != null) + { + this.bbox.grow(10); + } + } + + + + + this.pointerMoveHandler = mxUtils.bind(this, function(evt) + { + var src = mxEvent.getSource(evt); + + while (src != null) + { + if (src == this.currentElt) + { + return; + } + + src = src.parentNode; + } + + this.hideTooltip(); + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.pointerMoveHandler); + + // Handles mouse leaving the window + this.pointerOutHandler = mxUtils.bind(this, function(evt) + { + if (evt.toElement == null && evt.relatedTarget == null) + { + this.hideTooltip(); + } + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerout' : 'mouseout', this.pointerOutHandler); + + this.showTooltip(this ,this, 200, 200, "tooltip", true ) + } +}; + + +HoverIcons.prototype.getTooltipOffset = function() +{ + return new mxPoint(0, 0); +}; + + + + +/** + * + */ +HoverIcons.prototype.showTooltip = function(elt, title, showLabel) +{ + + // Lazy creation of the DOM nodes and graph instance + if (this.currentElt != elt) + { + this.tooltip = document.createElement('div'); + this.tooltip.className = 'geSidebarTooltip'; + this.tooltip.style.zIndex = mxPopupMenu.prototype.zIndex + 1; + this.tooltip.style.textAlign = 'center' + var tooltipHeight = 50 + + this.tooltip.style.width = '200px' + this.tooltip.createElement = 'hr' + + this.id = document.createElement('div') + var targetModel = OC_WORKSPACE[this.currentState.cell.rType].find(obj => {return obj.ID == this.currentState.cell.rID }) + this.id.innerHTML = targetModel.name + this.tooltip.appendChild(this.id) + + var hr = document.createElement('hr') + this.tooltip.appendChild(hr) + + //https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript + function bytesToSize(bytes) { + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes == 0) return '0 Byte'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; + } + + switch(this.currentState.cell.rType) { + case "computing": + tooltipHeight += 25 + cpus = document.createElement('div') + cpus.innerHTML="CPUs: " + targetModel.execution_requirements.cpus + this.tooltip.appendChild(cpus) + + tooltipHeight += 25 + ram = document.createElement('div') + ram.innerHTML="RAM: " + bytesToSize(1048576 * targetModel.execution_requirements.ram) + this.tooltip.appendChild(ram) + + tooltipHeight += 25 + gpus = document.createElement('div') + gpus.innerHTML="GPUs: " + targetModel.execution_requirements.gpus + this.tooltip.appendChild(gpus) + + + break; + case "storage": + tooltipHeight += 25 + capacity = document.createElement('div') + capacity.innerHTML="Capacity: " + bytesToSize(1048576 * targetModel.size) + this.tooltip.appendChild(capacity) + + tooltipHeight += 25 + performance = document.createElement('div') + performance.innerHTML="Performance: " + targetModel.throughput + this.tooltip.appendChild(performance) + + tooltipHeight += 25 + raid = document.createElement('div') + raid.innerHTML="RAID: " + targetModel.redundancy + this.tooltip.appendChild(raid) + + break; + case "datacenter": + tooltipHeight += 25 + cpus = document.createElement('div') + cpus.innerHTML="CPU cores: " + targetModel.cpu.cores + " (" + targetModel.cpu.architecture + ")" + this.tooltip.appendChild(cpus) + + tooltipHeight += 25 + ram = document.createElement('div') + ram.innerHTML="RAM: " + bytesToSize(1048576 * targetModel.ram.size) + this.tooltip.appendChild(ram) + + tooltipHeight += 25 + gpus = document.createElement('div') + gpus.innerHTML="GPUs: " + targetModel.gpu.length + this.tooltip.appendChild(gpus) + + break; + default: + // code block + } + + // // Get text + // Object.values( OC_WORKSPACE[this.currentState.cell.rType]).forEach(key => { + // if (key.ID == this.currentState.cell.rID) { + // this.rId.innerHTML = key.name + // } + // }); + + + + // this.tooltip.appendChild(this.execReq) + + + this.tooltip.style.height = tooltipHeight + 'px' + document.body.appendChild(this.tooltip); + + this.graph2 = new Graph(this.tooltip, null, null, MX_EDITORUI.editor.graph.getStylesheet()); + + this.graph2.resetViewOnRootChange = false; + this.graph2.foldingEnabled = false; + this.graph2.gridEnabled = false; + this.graph2.autoScroll = false; + this.graph2.setTooltips(false); + this.graph2.setConnectable(false); + this.graph2.setEnabled(false); + + if (!mxClient.IS_SVG) + { + this.graph2.view.canvas.style.position = 'relative'; + } + } + + // Workaround for ignored position CSS style in IE9 + // (changes to relative without the following line) + this.tooltip.style.position = 'absolute'; + this.tooltip.style.left = cursorX + 'px'; + this.tooltip.style.top = cursorY + 'px'; + + + this.currentElt = elt; + + +}; + +HoverIcons.prototype.hideTooltip = function() +{ + if (this.thread != null) + { + window.clearTimeout(this.thread); + this.thread = null; + } + + if (this.tooltip != null) + { + this.tooltip.style.display = 'none'; + this.currentElt = null; + } +}; + +/** + * + */ +HoverIcons.prototype.computeBoundingBox = function() +{ + var bbox = (!this.graph.model.isEdge(this.currentState.cell)) ? mxRectangle.fromRectangle(this.currentState) : null; + + this.visitNodes(function(elt) + { + if (elt.parentNode != null) + { + var tmp = new mxRectangle(elt.offsetLeft, elt.offsetTop, elt.offsetWidth, elt.offsetHeight); + + if (bbox == null) + { + bbox = tmp; + } + else + { + bbox.add(tmp); + } + } + }); + + return bbox; +}; + +/** + * + */ +HoverIcons.prototype.getState = function(state) +{ + if (state != null) + { + var cell = state.cell; + + if (!this.graph.getModel().contains(cell)) + { + state = null; + } + else + { + // Uses connectable parent vertex if child is not connectable + if (this.graph.getModel().isVertex(cell) && !this.graph.isCellConnectable(cell)) + { + var parent = this.graph.getModel().getParent(cell); + + if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent)) + { + cell = parent; + } + } + + // Ignores locked cells and edges + if (this.graph.isCellLocked(cell) || this.graph.model.isEdge(cell)) + { + cell = null; + } + + state = this.graph.view.getState(cell); + + if (state != null && state.style == null) + { + state = null; + } + } + } + + return state; +}; + +/** + * + */ +HoverIcons.prototype.update = function(state, x, y) +{ + if (!this.graph.connectionArrowsEnabled || (state != null && + mxUtils.getValue(state.style, 'allowArrows', '1') == '0')) + { + this.reset(); + } + else + { + if (state != null && state.cell.geometry != null && state.cell.geometry.relative && + this.graph.model.isEdge(state.cell.parent)) + { + state = null; + } + + var timeOnTarget = null; + + // Time on target + if (this.prev != state || this.isActive()) + { + this.startTime = new Date().getTime(); + this.prev = state; + timeOnTarget = 0; + + if (this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + if (state != null) + { + // Starts timer to update current state with no mouse events + this.updateThread = window.setTimeout(mxUtils.bind(this, function() + { + if (!this.isActive() && !this.graph.isMouseDown && + !this.graph.panningHandler.isActive()) + { + this.prev = state; + this.update(state, x, y); + } + }), this.updateDelay + 10); + } + } + else if (this.startTime != null) + { + timeOnTarget = new Date().getTime() - this.startTime; + } + + this.setDisplay(''); + + if (this.currentState != null && this.currentState != state && timeOnTarget < this.activationDelay && + this.bbox != null && !mxUtils.contains(this.bbox, x, y)) + { + this.reset(false); + } + else if (this.currentState != null || timeOnTarget > this.activationDelay) + { + if (this.currentState != state && ((timeOnTarget > this.updateDelay && state != null) || + this.bbox == null || x == null || y == null || !mxUtils.contains(this.bbox, x, y))) + { + if (state != null && this.graph.isEnabled()) + { + this.removeNodes(); + this.setCurrentState(state); + this.repaint(); + + // Resets connection points on other focused cells + if (this.graph.connectionHandler.constraintHandler.currentFocus != state) + { + this.graph.connectionHandler.constraintHandler.reset(); + } + } + else + { + this.reset(); + } + } + } + } +}; + +/** + * + */ +HoverIcons.prototype.setCurrentState = function(state) +{ + if (state.style['portConstraint'] != 'eastwest') + { + this.graph.container.appendChild(this.arrowUp); + this.graph.container.appendChild(this.arrowDown); + } + + this.graph.container.appendChild(this.arrowRight); + this.graph.container.appendChild(this.arrowLeft); + this.currentState = state; +}; + +/** + * Returns true if the given cell is a table. + */ +Graph.prototype.createParent = function(parent, child, childCount) +{ + parent = this.cloneCell(parent); + + for (var i = 0; i < childCount; i++) + { + parent.insert(this.cloneCell(child)); + } + + return parent; +}; + +/** + * Returns true if the given cell is a table. + */ +Graph.prototype.createTable = function(rowCount, colCount, w, h) +{ + w = (w != null) ? w : 40; + h = (h != null) ? h : 30; + + return this.createParent(this.createVertex(null, null, '', 0, 0, colCount * w, rowCount * h, + 'html=1;whiteSpace=wrap;container=1;collapsible=0;childLayout=tableLayout;'), + this.createParent(this.createVertex(null, null, '', 0, 0, colCount * w, h, + 'html=1;whiteSpace=wrap;container=1;collapsible=0;points=[[0,0.5],[1,0.5]];part=1;'), + this.createVertex(null, null, '', 0, 0, w, h, + 'html=1;whiteSpace=wrap;connectable=0;part=1;'), + colCount), rowCount); +}; + +/** + * + */ +Graph.prototype.createCrossFunctionalSwimlane = function(rowCount, colCount, w, h) +{ + w = (w != null) ? w : 120; + h = (h != null) ? h : 120; + + var s = 'swimlane;html=1;whiteSpace=wrap;container=1;' + + 'collapsible=0;recursiveResize=0;expand=0;'; + + var table = this.createVertex(null, null, '', + 0, 0, colCount * w, rowCount * h, + s + 'childLayout=tableLayout;'); + var row = this.createVertex(null, null, '', 0, 0, colCount * w, h, + s + 'horizontal=0;points=[[0,0.5],[1,0.5]];part=1;'); + table.insert(this.createParent(row, this.createVertex(null, null, '', + 0, 0, w, h, s + 'connectable=0;part=1;'), colCount)); + + if (rowCount > 1) + { + return this.createParent(table, this.createParent(row, + this.createVertex(null, null, '', 0, 0, w, h, + s + 'connectable=0;part=1;startSize=0;'), + colCount), rowCount - 1); + } + else + { + return table; + } +}; + +/** + * Returns true if the given cell is a table cell. + */ +Graph.prototype.isTableCell = function(cell) +{ + return this.isTableRow(this.model.getParent(cell)); +}; + +/** + * Returns true if the given cell is a table row. + */ +Graph.prototype.isTableRow = function(cell) +{ + return this.isTable(this.model.getParent(cell)); +}; + +/** + * Returns true if the given cell is a table. + */ +Graph.prototype.isTable = function(cell) +{ + var style = this.getCellStyle(cell); + + return style != null && style['childLayout'] == 'tableLayout'; +}; + +/** + * Updates column width and row height. + */ +Graph.prototype.getActualStartSize = function(swimlane, ignoreState) +{ + var result = new mxRectangle(); + + if (this.isSwimlane(swimlane)) + { + var style = this.getCurrentCellStyle(swimlane, ignoreState); + var size = parseInt(mxUtils.getValue(style, + mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); + var flipH = mxUtils.getValue(style, mxConstants.STYLE_FLIPH, 0) == 1; + var flipV = mxUtils.getValue(style, mxConstants.STYLE_FLIPV, 0) == 1; + var h = mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true); + var n = 0; + + if (!h) + { + n++; + } + + var dir = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST); + + if (dir == mxConstants.DIRECTION_NORTH) + { + n++; + } + else if (dir == mxConstants.DIRECTION_WEST) + { + n += 2; + } + else if (dir == mxConstants.DIRECTION_SOUTH) + { + n += 3; + } + + n = mxUtils.mod(n, 4); + + if (n == 0) + { + result.y = size; + } + else if (n == 1) + { + result.x = size; + } + else if (n == 2) + { + result.height = size; + } + else if (n == 3) + { + result.width = size; + } + + if (flipV) + { + var tmp = result.y; + result.y = result.height; + result.height = tmp; + } + + if (flipH) + { + var tmp = result.x; + result.x = result.width; + result.width = tmp; + } + } + + return result; +}; + +/** + * Updates column width and row height. + */ +Graph.prototype.tableResized = function(table) +{ + console.log('tableLayout.tableResized', table); + var model = this.getModel(); + var rowCount = model.getChildCount(table); + var tableGeo = this.getCellGeometry(table); + + if (tableGeo != null && rowCount > 0) + { + var off = this.getActualStartSize(table); + var y = off.y; + + for (var i = 0; i < rowCount; i++) + { + var row = model.getChildAt(table, i); + + if (row != null && model.isVertex(row)) + { + var rowGeo = this.getCellGeometry(row); + + if (rowGeo != null) + { + if (i == rowCount - 1) + { + var newRowGeo = rowGeo.clone(); + newRowGeo.width = tableGeo.width - off.x; + + if (y < tableGeo.height) + { + newRowGeo.height = tableGeo.height - y; + } + else if (y > tableGeo.height) + { + tableGeo.height = y + Graph.minTableRowHeight; + newRowGeo.height = Graph.minTableRowHeight; + } + + model.setGeometry(row, newRowGeo); + this.tableRowResized(row, newRowGeo, rowGeo); + } + + y += rowGeo.height; + } + } + } + } +}; + +/** + * Updates column width and row height. + */ +Graph.prototype.setRowHeight = function(row, height) +{ + var model = this.getModel(); + + model.beginUpdate(); + try + { + for (var i = 0; i < model.getChildCount(row); i++) + { + var child = model.getChildAt(row, i); + + if (model.isVertex(child)) + { + var childGeo = this.getCellGeometry(child); + + if (childGeo != null) + { + childGeo = childGeo.clone(); + childGeo.height = height; + model.setGeometry(child, childGeo); + } + } + } + } + finally + { + model.endUpdate(); + } +}; + +/** + * Updates column width and row height. + */ +Graph.prototype.tableRowResized = function(row, bounds, prev) +{ + console.log('tableLayout.tableRowResized', row); + var model = this.getModel(); + var rowGeo = this.getCellGeometry(row); + var cellCount = model.getChildCount(row); + + if (rowGeo != null && cellCount > 0) + { + var off = this.getActualStartSize(row); + var x = off.x; + + for (var i = 0; i < cellCount; i++) + { + var cell = model.getChildAt(row, i); + + if (cell != null && model.isVertex(cell)) + { + var geo = this.getCellGeometry(cell); + + if (geo != null) + { + var newGeo = geo.clone(); + newGeo.height = rowGeo.height - off.y; + model.setGeometry(cell, newGeo); + + if (i == cellCount - 1) + { + if (x < rowGeo.width) + { + newGeo.width = rowGeo.width - x; + } + else if (x > rowGeo.width) + { + rowGeo.width = x + Graph.minTableColumnWidth; + newGeo.width = Graph.minTableColumnWidth; + } + + this.tableCellResized(cell, newGeo, geo); + } + + x += geo.width; + } + } + } + } + + // Updates previous row height if upper edge was moved + var table = model.getParent(row); + var index = table.getIndex(row); + + if (bounds.y != prev.y && index > 0) + { + var previousRow = model.getChildAt(table, index - 1); + var prg = this.getCellGeometry(previousRow); + + if (prg != null) + { + prg = prg.clone(); + prg.height -= prev.y - bounds.y; + model.setGeometry(previousRow, prg); + } + } +}; + +/** + * Updates column width and row height. + */ +Graph.prototype.tableCellResized = function(cell, bounds, prev) +{ + console.log('tableLayout.tableCellResized', cell, bounds, prev); + var geo = this.getCellGeometry(cell); + + if (geo != null) + { + var model = this.getModel(); + var row = model.getParent(cell); + var table = model.getParent(row); + var index = row.getIndex(cell); + + // Applies new height to all cells in the row + if (bounds.height != prev.height) + { + this.setRowHeight(row, geo.height); + } + + // Updates column width + var previousRow = null; + + for (var i = 0; i < model.getChildCount(table); i++) + { + var currentRow = model.getChildAt(table, i); + + if (model.isVertex(currentRow)) + { + var child = model.getChildAt(currentRow, index); + + if (cell != child) + { + var childGeo = this.getCellGeometry(child); + + if (childGeo != null) + { + childGeo = childGeo.clone(); + childGeo.width = geo.width; + model.setGeometry(child, childGeo); + } + } + + // Updates previous row height + if (bounds.y != prev.y && currentRow == row && previousRow != null) + { + var prg = this.getCellGeometry(previousRow); + + if (prg != null) + { + this.setRowHeight(previousRow, + prg.height - prev.y + bounds.y); + } + } + + previousRow = currentRow; + } + } + + // Updates previous column width + if (bounds.x != prev.x && index > 0) + { + var child = model.getChildAt(row, index - 1); + var childGeo = this.getCellGeometry(child); + + if (childGeo != null) + { + var newChildGeo = childGeo.clone(); + newChildGeo.width -= prev.x - bounds.x; + model.setGeometry(child, newChildGeo); + + this.tableCellResized(child, newChildGeo, childGeo); + } + } + } +}; + +/** + * Table Layout + */ +function TableLayout(graph) +{ + mxGraphLayout.call(this, graph); +}; + +/** + * Extends mxGraphLayout + */ +TableLayout.prototype = new mxGraphLayout(); +TableLayout.prototype.constructor = TableLayout; + +/** + * Reorders rows. + */ +TableLayout.prototype.moveCell = function(cell, x, y) +{ + // TODO: Reorder rows + console.log('tableLayout.moveCell', cell, x, y); +}; + +/** + * + */ +TableLayout.prototype.resizeCell = function(cell, geo, prev) +{ + if (this.graph.isTable(cell)) + { + this.graph.tableResized(cell, geo, prev); + } + else if (this.graph.isTableRow(cell)) + { + this.graph.tableRowResized(cell, geo, prev); + } + else if (this.graph.isTableCell(cell)) + { + this.graph.tableCellResized(cell, geo, prev); + } +}; + +/** + * Updates column width and row height. + */ +TableLayout.prototype.execute = function(table) +{ + var off = this.graph.getActualStartSize(table, true); + var model = this.graph.getModel(); + var y = off.y; + var rows = []; + var maxX = 0; + var x = 0; + + console.log('tableLayout.execute', table, off); + + for (var i = 0; i < model.getChildCount(table); i++) + { + var row = model.getChildAt(table, i); + + if (row != null && model.isVertex(row)) + { + var rowGeo = this.graph.getCellGeometry(row); + + if (rowGeo != null) + { + rowGeo = rowGeo.clone(); + var rowOff = this.graph.getActualStartSize(row, true); + var childCount = model.getChildCount(row); + x = rowOff.x; + + for (var j = 0; j < childCount; j++) + { + var cell = model.getChildAt(row, j); + + if (cell != null) + { + var geo = this.graph.getCellGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + + rowGeo.height = geo.height + + rowOff.y + rowOff.height; + geo.x = x; + geo.y = rowOff.y; + model.setGeometry(cell, geo); + + x += geo.width; + + if (j == childCount - 1) + { + rows.push([geo, rowGeo, rowOff]); + maxX = Math.max(x, maxX); + } + } + } + } + + rowGeo.width = x + rowOff.width; + rowGeo.y = y; + rowGeo.x = off.x; + model.setGeometry(row, rowGeo); + + y += rowGeo.height; + } + } + } + + // Updates table size + var tableGeo = this.graph.getCellGeometry(table); + + if (tableGeo != null) + { + tableGeo = tableGeo.clone(); + tableGeo.width = x + off.x + off.width; + tableGeo.height = y + off.height; + model.setGeometry(table, tableGeo); + + for (var i = 0; i < rows.length; i++) + { + if (rows[i][1].width < maxX) + { + rows[i][0].width += maxX - rows[i][1].width; + rows[i][1].width += maxX - rows[i][1].width; + } + } + } +}; + +/** + * Table Layout + */ +function TableRowLayout(graph) +{ + mxGraphLayout.call(this, graph); +}; + +/** + * Extends mxGraphLayout + */ +TableRowLayout.prototype = new TableLayout(); +TableRowLayout.prototype.constructor = TableRowLayout; + +/** + * Reorders rows. + */ +TableRowLayout.prototype.moveCell = function(cell, x, y) +{ + // TODO: Reorder columns + console.log('TableRowLayout.moveCell', cell, x, y); +}; + +/** + * Updates row start sizes. + */ +TableRowLayout.prototype.execute = function(row) +{ + var off = this.graph.getActualStartSize(row, true); + var off0 = this.graph.getActualStartSize(row); + var style = this.graph.getCellStyle(row); + var model = this.graph.getModel(); + var table = model.getParent(row); + + console.log('tableRowLayout.execute', row, off, off0); + + if (style != null && table != null) + { + var size = parseInt(mxUtils.getValue(style, + mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); + + model.beginUpdate(); + try + { + // Swimlane rotation requires resize of last row element + if (this.graph.isTableRow(row)) + { + if (off.width != off0.width || off.x != off0.x) + { + var cell = model.getChildAt(row, model.getChildCount(row) - 1); + var geo = this.graph.getCellGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.width += off0.width - off.width - off.x - off0.x; + geo.width = Math.max(Graph.minTableColumnWidth, geo.width); + model.setGeometry(cell, geo); + } + } + } + + for (var i = 0; i < model.getChildCount(table); i++) + { + var current = model.getChildAt(table, i); + + if (current != row) + { + var temp = this.graph.getActualStartSize(current); + + // Checks if same side is offset + if ((off.x > 0 && temp.x > 0) || + (off.y > 0 && temp.y > 0) || + (off.width > 0 && temp.width > 0) || + (off.height > 0 && temp.height > 0)) + { + this.graph.setCellStyles( + mxConstants.STYLE_STARTSIZE, + size, [current]); + } + } + } + } + finally + { + model.endUpdate(); + } + } +}; + +(function() +{ + /** + * Reset the list of processed edges. + */ + var mxGraphViewResetValidationState = mxGraphView.prototype.resetValidationState; + + mxGraphView.prototype.resetValidationState = function() + { + mxGraphViewResetValidationState.apply(this, arguments); + + this.validEdges = []; + }; + + /** + * Updates jumps for valid edges and repaints if needed. + */ + var mxGraphViewValidateCellState = mxGraphView.prototype.validateCellState; + + mxGraphView.prototype.validateCellState = function(cell, recurse) + { + recurse = (recurse != null) ? recurse : true; + var state = this.getState(cell); + + // Forces repaint if jumps change on a valid edge + if (state != null && recurse && this.graph.model.isEdge(state.cell) && + state.style != null && state.style[mxConstants.STYLE_CURVED] != 1 && + !state.invalid && this.updateLineJumps(state)) + { + this.graph.cellRenderer.redraw(state, false, this.isRendering()); + } + + state = mxGraphViewValidateCellState.apply(this, arguments); + + // Adds to the list of edges that may intersect with later edges + if (state != null && recurse && this.graph.model.isEdge(state.cell) && + state.style != null && state.style[mxConstants.STYLE_CURVED] != 1) + { + // LATER: Reuse jumps for valid edges + this.validEdges.push(state); + } + + return state; + }; + + /** + * Forces repaint if routed points have changed. + */ + var mxCellRendererIsShapeInvalid = mxCellRenderer.prototype.isShapeInvalid; + + mxCellRenderer.prototype.isShapeInvalid = function(state, shape) + { + return mxCellRendererIsShapeInvalid.apply(this, arguments) || + (state.routedPoints != null && shape.routedPoints != null && + !mxUtils.equalPoints(shape.routedPoints, state.routedPoints)) + }; + + + /** + * Updates jumps for invalid edges. + */ + var mxGraphViewUpdateCellState = mxGraphView.prototype.updateCellState; + + mxGraphView.prototype.updateCellState = function(state) + { + mxGraphViewUpdateCellState.apply(this, arguments); + + // Updates jumps on invalid edge before repaint + if (this.graph.model.isEdge(state.cell) && + state.style[mxConstants.STYLE_CURVED] != 1) + { + this.updateLineJumps(state); + } + }; + + /** + * Updates the jumps between given state and processed edges. + */ + mxGraphView.prototype.updateLineJumps = function(state) + { + var pts = state.absolutePoints; + + if (Graph.lineJumpsEnabled) + { + var changed = state.routedPoints != null; + var actual = null; + + if (pts != null && this.validEdges != null && + mxUtils.getValue(state.style, 'jumpStyle', 'none') !== 'none') + { + var thresh = 0.5 * this.scale; + changed = false; + actual = []; + + // Type 0 means normal waypoint, 1 means jump + function addPoint(type, x, y) + { + var rpt = new mxPoint(x, y); + rpt.type = type; + + actual.push(rpt); + var curr = (state.routedPoints != null) ? state.routedPoints[actual.length - 1] : null; + + return curr == null || curr.type != type || curr.x != x || curr.y != y; + }; + + for (var i = 0; i < pts.length - 1; i++) + { + var p1 = pts[i + 1]; + var p0 = pts[i]; + var list = []; + + // Ignores waypoints on straight segments + var pn = pts[i + 2]; + + while (i < pts.length - 2 && + mxUtils.ptSegDistSq(p0.x, p0.y, pn.x, pn.y, + p1.x, p1.y) < 1 * this.scale * this.scale) + { + p1 = pn; + i++; + pn = pts[i + 2]; + } + + changed = addPoint(0, p0.x, p0.y) || changed; + + // Processes all previous edges + for (var e = 0; e < this.validEdges.length; e++) + { + var state2 = this.validEdges[e]; + var pts2 = state2.absolutePoints; + + if (pts2 != null && mxUtils.intersects(state, state2) && state2.style['noJump'] != '1') + { + // Compares each segment of the edge with the current segment + for (var j = 0; j < pts2.length - 1; j++) + { + var p3 = pts2[j + 1]; + var p2 = pts2[j]; + + // Ignores waypoints on straight segments + pn = pts2[j + 2]; + + while (j < pts2.length - 2 && + mxUtils.ptSegDistSq(p2.x, p2.y, pn.x, pn.y, + p3.x, p3.y) < 1 * this.scale * this.scale) + { + p3 = pn; + j++; + pn = pts2[j + 2]; + } + + var pt = mxUtils.intersection(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + + // Handles intersection between two segments + if (pt != null && (Math.abs(pt.x - p0.x) > thresh || + Math.abs(pt.y - p0.y) > thresh) && + (Math.abs(pt.x - p1.x) > thresh || + Math.abs(pt.y - p1.y) > thresh) && + (Math.abs(pt.x - p2.x) > thresh || + Math.abs(pt.y - p2.y) > thresh) && + (Math.abs(pt.x - p3.x) > thresh || + Math.abs(pt.y - p3.y) > thresh)) + { + var dx = pt.x - p0.x; + var dy = pt.y - p0.y; + var temp = {distSq: dx * dx + dy * dy, x: pt.x, y: pt.y}; + + // Intersections must be ordered by distance from start of segment + for (var t = 0; t < list.length; t++) + { + if (list[t].distSq > temp.distSq) + { + list.splice(t, 0, temp); + temp = null; + + break; + } + } + + // Ignores multiple intersections at segment joint + if (temp != null && (list.length == 0 || + list[list.length - 1].x !== temp.x || + list[list.length - 1].y !== temp.y)) + { + list.push(temp); + } + } + } + } + } + + // Adds ordered intersections to routed points + for (var j = 0; j < list.length; j++) + { + changed = addPoint(1, list[j].x, list[j].y) || changed; + } + } + + var pt = pts[pts.length - 1]; + changed = addPoint(0, pt.x, pt.y) || changed; + } + + state.routedPoints = actual; + + return changed; + } + else + { + return false; + } + }; + + /** + * Overrides painting the actual shape for taking into account jump style. + */ + var mxConnectorPaintLine = mxConnector.prototype.paintLine; + + mxConnector.prototype.paintLine = function (c, absPts, rounded) + { + // Required for checking dirty state + this.routedPoints = (this.state != null) ? this.state.routedPoints : null; + + if (this.outline || this.state == null || this.style == null || + this.state.routedPoints == null || this.state.routedPoints.length == 0) + { + mxConnectorPaintLine.apply(this, arguments); + } + else + { + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.LINE_ARCSIZE) / 2; + var size = (parseInt(mxUtils.getValue(this.style, 'jumpSize', + Graph.defaultJumpSize)) - 2) / 2 + this.strokewidth; + var style = mxUtils.getValue(this.style, 'jumpStyle', 'none'); + var moveTo = true; + var last = null; + var len = null; + var pts = []; + var n = null; + c.begin(); + + for (var i = 0; i < this.state.routedPoints.length; i++) + { + var rpt = this.state.routedPoints[i]; + var pt = new mxPoint(rpt.x / this.scale, rpt.y / this.scale); + + // Takes first and last point from passed-in array + if (i == 0) + { + pt = absPts[0]; + } + else if (i == this.state.routedPoints.length - 1) + { + pt = absPts[absPts.length - 1]; + } + + var done = false; + + // Type 1 is an intersection + if (last != null && rpt.type == 1) + { + // Checks if next/previous points are too close + var next = this.state.routedPoints[i + 1]; + var dx = next.x / this.scale - pt.x; + var dy = next.y / this.scale - pt.y; + var dist = dx * dx + dy * dy; + + if (n == null) + { + n = new mxPoint(pt.x - last.x, pt.y - last.y); + len = Math.sqrt(n.x * n.x + n.y * n.y); + + if (len > 0) + { + n.x = n.x * size / len; + n.y = n.y * size / len; + } + else + { + n = null; + } + } + + if (dist > size * size && len > 0) + { + var dx = last.x - pt.x; + var dy = last.y - pt.y; + var dist = dx * dx + dy * dy; + + if (dist > size * size) + { + var p0 = new mxPoint(pt.x - n.x, pt.y - n.y); + var p1 = new mxPoint(pt.x + n.x, pt.y + n.y); + pts.push(p0); + + this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); + + var f = (Math.round(n.x) < 0 || (Math.round(n.x) == 0 + && Math.round(n.y) <= 0)) ? 1 : -1; + moveTo = false; + + if (style == 'sharp') + { + c.lineTo(p0.x - n.y * f, p0.y + n.x * f); + c.lineTo(p1.x - n.y * f, p1.y + n.x * f); + c.lineTo(p1.x, p1.y); + } + else if (style == 'arc') + { + f *= 1.3; + c.curveTo(p0.x - n.y * f, p0.y + n.x * f, + p1.x - n.y * f, p1.y + n.x * f, + p1.x, p1.y); + } + else + { + c.moveTo(p1.x, p1.y); + moveTo = true; + } + + pts = [p1]; + done = true; + } + } + } + else + { + n = null; + } + + if (!done) + { + pts.push(pt); + last = pt; + } + } + + this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); + c.stroke(); + } + }; + + /** + * Adds support for snapToPoint style. + */ + var mxGraphViewUpdateFloatingTerminalPoint = mxGraphView.prototype.updateFloatingTerminalPoint; + + mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source) + { + if (start != null && edge != null && + (start.style['snapToPoint'] == '1' || + edge.style['snapToPoint'] == '1')) + { + start = this.getTerminalPort(edge, start, source); + var next = this.getNextPoint(edge, end, source); + + var orth = this.graph.isOrthogonal(edge); + var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0')); + var center = new mxPoint(start.getCenterX(), start.getCenterY()); + + if (alpha != 0) + { + var cos = Math.cos(-alpha); + var sin = Math.sin(-alpha); + next = mxUtils.getRotatedPoint(next, cos, sin, center); + } + + var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0); + border += parseFloat(edge.style[(source) ? + mxConstants.STYLE_SOURCE_PERIMETER_SPACING : + mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0); + var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border); + + if (alpha != 0) + { + var cos = Math.cos(alpha); + var sin = Math.sin(alpha); + pt = mxUtils.getRotatedPoint(pt, cos, sin, center); + } + + edge.setAbsoluteTerminalPoint(this.snapToAnchorPoint(edge, start, end, source, pt), source); + } + else + { + mxGraphViewUpdateFloatingTerminalPoint.apply(this, arguments); + } + }; + + mxGraphView.prototype.snapToAnchorPoint = function(edge, start, end, source, pt) + { + if (start != null && edge != null) + { + var constraints = this.graph.getAllConnectionConstraints(start) + var nearest = null; + var dist = null; + + if (constraints != null) + { + for (var i = 0; i < constraints.length; i++) + { + var cp = this.graph.getConnectionPoint(start, constraints[i]); + + if (cp != null) + { + var tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y); + + if (dist == null || tmp < dist) + { + nearest = cp; + dist = tmp; + } + } + } + } + + if (nearest != null) + { + pt = nearest; + } + } + + return pt; + }; + + /** + * Adds support for placeholders in text elements of shapes. + */ + var mxStencilEvaluateTextAttribute = mxStencil.prototype.evaluateTextAttribute; + + mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape) + { + var result = mxStencilEvaluateTextAttribute.apply(this, arguments); + var placeholders = node.getAttribute('placeholders'); + + if (placeholders == '1' && shape.state != null) + { + result = shape.state.view.graph.replacePlaceholders(shape.state.cell, result); + } + + return result; + }; + + /** + * Adds custom stencils defined via shape=stencil(value) style. The value is a base64 encoded, compressed and + * URL encoded XML definition of the shape according to the stencil definition language of mxGraph. + * + * Needs to be in this file to make sure its part of the embed client code. Also the check for ZLib is + * different than for the Editor code. + */ + var mxCellRendererCreateShape = mxCellRenderer.prototype.createShape; + mxCellRenderer.prototype.createShape = function(state) + { + if (state.style != null && typeof(pako) !== 'undefined') + { + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + // Extracts and decodes stencil XML if shape has the form shape=stencil(value) + if (shape != null && typeof shape === 'string' && shape.substring(0, 8) == 'stencil(') + { + try + { + var stencil = shape.substring(8, shape.length - 1); + var doc = mxUtils.parseXml(Graph.decompress(stencil)); + + return new mxShape(new mxStencil(doc.documentElement)); + } + catch (e) + { + if (window.console != null) + { + console.log('Error in shape: ' + e); + } + } + } + } + + return mxCellRendererCreateShape.apply(this, arguments); + }; +})(); + +/** + * Overrides stencil registry for dynamic loading of stencils. + */ +/** + * Maps from library names to an array of Javascript filenames, + * which are synchronously loaded. Currently only stencil files + * (.xml) and JS files (.js) are supported. + * IMPORTANT: For embedded diagrams to work entries must also + * be added in EmbedServlet.java. + */ +mxStencilRegistry.libraries = {}; + +/** + * Global switch to disable dynamic loading. + */ +mxStencilRegistry.dynamicLoading = true; + +/** + * Global switch to disable eval for JS (preload all JS instead). + */ +mxStencilRegistry.allowEval = true; + +/** + * Stores all package names that have been dynamically loaded. + * Each package is only loaded once. + */ +mxStencilRegistry.packages = []; + +// Extends the default stencil registry to add dynamic loading +mxStencilRegistry.getStencil = function(name) +{ + var result = mxStencilRegistry.stencils[name]; + + if (result == null && mxCellRenderer.defaultShapes[name] == null && mxStencilRegistry.dynamicLoading) + { + var basename = mxStencilRegistry.getBasenameForStencil(name); + + // Loads stencil files and tries again + if (basename != null) + { + var libs = mxStencilRegistry.libraries[basename]; + + if (libs != null) + { + if (mxStencilRegistry.packages[basename] == null) + { + for (var i = 0; i < libs.length; i++) + { + var fname = libs[i]; + + if (fname.toLowerCase().substring(fname.length - 4, fname.length) == '.xml') + { + mxStencilRegistry.loadStencilSet(fname, null); + } + else if (fname.toLowerCase().substring(fname.length - 3, fname.length) == '.js') + { + try + { + if (mxStencilRegistry.allowEval) + { + var req = mxUtils.load(fname); + + if (req != null && req.getStatus() >= 200 && req.getStatus() <= 299) + { + eval.call(window, req.getText()); + } + } + } + catch (e) + { + if (window.console != null) + { + console.log('error in getStencil:', fname, e); + } + } + } + else + { + // FIXME: This does not yet work as the loading is triggered after + // the shape was used in the graph, at which point the keys have + // typically been translated in the calling method. + //mxResources.add(fname); + } + } + + mxStencilRegistry.packages[basename] = 1; + } + } + else + { + // Replaces '_-_' with '_' + basename = basename.replace('_-_', '_'); + mxStencilRegistry.loadStencilSet(STENCIL_PATH + '/' + basename + '.xml', null); + } + + result = mxStencilRegistry.stencils[name]; + } + } + + return result; +}; + +// Returns the basename for the given stencil or null if no file must be +// loaded to render the given stencil. +mxStencilRegistry.getBasenameForStencil = function(name) +{ + var tmp = null; + + if (name != null && typeof name === 'string') + { + var parts = name.split('.'); + + if (parts.length > 0 && parts[0] == 'mxgraph') + { + tmp = parts[1]; + + for (var i = 2; i < parts.length - 1; i++) + { + tmp += '/' + parts[i]; + } + } + } + + return tmp; +}; + +// Loads the given stencil set +mxStencilRegistry.loadStencilSet = function(stencilFile, postStencilLoad, force, async) +{ + force = (force != null) ? force : false; + + // Uses additional cache for detecting previous load attempts + var xmlDoc = mxStencilRegistry.packages[stencilFile]; + + if (force || xmlDoc == null) + { + var install = false; + + if (xmlDoc == null) + { + try + { + if (async) + { + mxStencilRegistry.loadStencil(stencilFile, mxUtils.bind(this, function(xmlDoc2) + { + if (xmlDoc2 != null && xmlDoc2.documentElement != null) + { + mxStencilRegistry.packages[stencilFile] = xmlDoc2; + install = true; + mxStencilRegistry.parseStencilSet(xmlDoc2.documentElement, postStencilLoad, install); + } + })); + + return; + } + else + { + xmlDoc = mxStencilRegistry.loadStencil(stencilFile); + mxStencilRegistry.packages[stencilFile] = xmlDoc; + install = true; + } + } + catch (e) + { + if (window.console != null) + { + console.log('error in loadStencilSet:', stencilFile, e); + } + } + } + + if (xmlDoc != null && xmlDoc.documentElement != null) + { + mxStencilRegistry.parseStencilSet(xmlDoc.documentElement, postStencilLoad, install); + } + } +}; + +// Loads the given stencil XML file. +mxStencilRegistry.loadStencil = function(filename, fn) +{ + if (fn != null) + { + var req = mxUtils.get(filename, mxUtils.bind(this, function(req) + { + fn((req.getStatus() >= 200 && req.getStatus() <= 299) ? req.getXml() : null); + })); + } + else + { + return mxUtils.load(filename).getXml(); + } +}; + +// Takes array of strings +mxStencilRegistry.parseStencilSets = function(stencils) +{ + for (var i = 0; i < stencils.length; i++) + { + mxStencilRegistry.parseStencilSet(mxUtils.parseXml(stencils[i]).documentElement); + } +}; + +// Parses the given stencil set +mxStencilRegistry.parseStencilSet = function(root, postStencilLoad, install) +{ + if (root.nodeName == 'stencils') + { + var shapes = root.firstChild; + + while (shapes != null) + { + if (shapes.nodeName == 'shapes') + { + mxStencilRegistry.parseStencilSet(shapes, postStencilLoad, install); + } + + shapes = shapes.nextSibling; + } + } + else + { + install = (install != null) ? install : true; + var shape = root.firstChild; + var packageName = ''; + var name = root.getAttribute('name'); + + if (name != null) + { + packageName = name + '.'; + } + + while (shape != null) + { + if (shape.nodeType == mxConstants.NODETYPE_ELEMENT) + { + name = shape.getAttribute('name'); + + if (name != null) + { + packageName = packageName.toLowerCase(); + var stencilName = name.replace(/ /g,"_"); + + if (install) + { + mxStencilRegistry.addStencil(packageName + stencilName.toLowerCase(), new mxStencil(shape)); + } + + if (postStencilLoad != null) + { + var w = shape.getAttribute('w'); + var h = shape.getAttribute('h'); + + w = (w == null) ? 80 : parseInt(w, 10); + h = (h == null) ? 80 : parseInt(h, 10); + + postStencilLoad(packageName, stencilName, name, w, h); + } + } + } + + shape = shape.nextSibling; + } + } +}; + +/** + * These overrides are only added if mxVertexHandler is defined (ie. not in embedded graph) + */ +if (typeof mxVertexHandler != 'undefined') +{ + (function() + { + // Sets colors for handles + mxConstants.HANDLE_FILLCOLOR = '#29b6f2'; + mxConstants.HANDLE_STROKECOLOR = '#0088cf'; + mxConstants.VERTEX_SELECTION_COLOR = '#00a8ff'; + mxConstants.OUTLINE_COLOR = '#00a8ff'; + mxConstants.OUTLINE_HANDLE_FILLCOLOR = '#99ccff'; + mxConstants.OUTLINE_HANDLE_STROKECOLOR = '#00a8ff'; + mxConstants.CONNECT_HANDLE_FILLCOLOR = '#cee7ff'; + mxConstants.EDGE_SELECTION_COLOR = '#00a8ff'; + mxConstants.DEFAULT_VALID_COLOR = '#00a8ff'; + mxConstants.LABEL_HANDLE_FILLCOLOR = '#cee7ff'; + mxConstants.GUIDE_COLOR = '#0088cf'; + mxConstants.HIGHLIGHT_OPACITY = 30; + mxConstants.HIGHLIGHT_SIZE = 5; + + // Enables snapping to off-grid terminals for edge waypoints + mxEdgeHandler.prototype.snapToTerminals = true; + + // Enables guides + mxGraphHandler.prototype.guidesEnabled = true; + + // Removes parents where all child cells are moved out + mxGraphHandler.prototype.removeEmptyParents = true; + + // Enables fading of rubberband + mxRubberband.prototype.fadeOut = true; + + // Alt-move disables guides + mxGuide.prototype.isEnabledForEvent = function(evt) + { + return !mxEvent.isAltDown(evt); + }; + + // Extends connection handler to enable ctrl+drag for cloning source cell + // since copyOnConnect is now disabled by default + var mxConnectionHandlerCreateTarget = mxConnectionHandler.prototype.isCreateTarget; + mxConnectionHandler.prototype.isCreateTarget = function(evt) + { + return mxEvent.isControlDown(evt) || mxConnectionHandlerCreateTarget.apply(this, arguments); + }; + + // Overrides highlight shape for connection points + mxConstraintHandler.prototype.createHighlightShape = function() + { + var hl = new mxEllipse(null, this.highlightColor, this.highlightColor, 0); + hl.opacity = mxConstants.HIGHLIGHT_OPACITY; + + return hl; + }; + + // Overrides edge preview to use current edge shape and default style + mxConnectionHandler.prototype.livePreview = true; + mxConnectionHandler.prototype.cursor = 'crosshair'; + + // Uses current edge style for connect preview + mxConnectionHandler.prototype.createEdgeState = function(me) + { + var style = this.graph.createCurrentEdgeStyle(); + var edge = this.graph.createEdge(null, null, null, null, null, style); + var state = new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge)); + + for (var key in this.graph.currentEdgeStyle) + { + state.style[key] = this.graph.currentEdgeStyle[key]; + } + + return state; + }; + + // Overrides dashed state with current edge style + var connectionHandlerCreateShape = mxConnectionHandler.prototype.createShape; + mxConnectionHandler.prototype.createShape = function() + { + var shape = connectionHandlerCreateShape.apply(this, arguments); + + shape.isDashed = this.graph.currentEdgeStyle[mxConstants.STYLE_DASHED] == '1'; + + return shape; + } + + // Overrides live preview to keep current style + mxConnectionHandler.prototype.updatePreview = function(valid) + { + // do not change color of preview + }; + + // Overrides connection handler to ignore edges instead of not allowing connections + var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; + mxConnectionHandler.prototype.createMarker = function() + { + var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); + + var markerGetCell = marker.getCell; + marker.getCell = mxUtils.bind(this, function(me) + { + var result = markerGetCell.apply(this, arguments); + + this.error = null; + + return result; + }); + + return marker; + }; + + /** + * + */ + Graph.prototype.defaultVertexStyle = {}; + + /** + * Contains the default style for edges. + */ + Graph.prototype.defaultEdgeStyle = {'edgeStyle': 'orthogonalEdgeStyle', 'rounded': '0', + 'jettySize': 'auto', 'orthogonalLoop': '1'}; + + /** + * Returns the current edge style as a string. + */ + Graph.prototype.createCurrentEdgeStyle = function() + { + var style = 'edgeStyle=' + (this.currentEdgeStyle['edgeStyle'] || 'none') + ';'; + + if (this.currentEdgeStyle['shape'] != null) + { + style += 'shape=' + this.currentEdgeStyle['shape'] + ';'; + } + + if (this.currentEdgeStyle['curved'] != null) + { + style += 'curved=' + this.currentEdgeStyle['curved'] + ';'; + } + + if (this.currentEdgeStyle['rounded'] != null) + { + style += 'rounded=' + this.currentEdgeStyle['rounded'] + ';'; + } + + if (this.currentEdgeStyle['comic'] != null) + { + style += 'comic=' + this.currentEdgeStyle['comic'] + ';'; + } + + if (this.currentEdgeStyle['jumpStyle'] != null) + { + style += 'jumpStyle=' + this.currentEdgeStyle['jumpStyle'] + ';'; + } + + if (this.currentEdgeStyle['jumpSize'] != null) + { + style += 'jumpSize=' + this.currentEdgeStyle['jumpSize'] + ';'; + } + + // Overrides the global default to match the default edge style + if (this.currentEdgeStyle['orthogonalLoop'] != null) + { + style += 'orthogonalLoop=' + this.currentEdgeStyle['orthogonalLoop'] + ';'; + } + else if (Graph.prototype.defaultEdgeStyle['orthogonalLoop'] != null) + { + style += 'orthogonalLoop=' + Graph.prototype.defaultEdgeStyle['orthogonalLoop'] + ';'; + } + + // Overrides the global default to match the default edge style + if (this.currentEdgeStyle['jettySize'] != null) + { + style += 'jettySize=' + this.currentEdgeStyle['jettySize'] + ';'; + } + else if (Graph.prototype.defaultEdgeStyle['jettySize'] != null) + { + style += 'jettySize=' + Graph.prototype.defaultEdgeStyle['jettySize'] + ';'; + } + + // Special logic for custom property of elbowEdgeStyle + if (this.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle' && this.currentEdgeStyle['elbow'] != null) + { + style += 'elbow=' + this.currentEdgeStyle['elbow'] + ';'; + } + + if (this.currentEdgeStyle['html'] != null) + { + style += 'html=' + this.currentEdgeStyle['html'] + ';'; + } + else + { + style += 'html=1;'; + } + + return style; + }; + + /** + * Hook for subclassers. + */ + Graph.prototype.getPagePadding = function() + { + return new mxPoint(0, 0); + }; + + /** + * Loads the stylesheet for this graph. + */ + Graph.prototype.loadStylesheet = function() + { + var node = (this.themes != null) ? this.themes[this.defaultThemeName] : + (!mxStyleRegistry.dynamicLoading) ? null : + mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement(); + + if (node != null) + { + var dec = new mxCodec(node.ownerDocument); + dec.decode(node, this.getStylesheet()); + } + }; + + /** + * Creates lookup from object IDs to cell IDs. + */ + Graph.prototype.createCellLookup = function(cells, lookup) + { + lookup = (lookup != null) ? lookup : new Object(); + + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + lookup[mxObjectIdentity.get(cell)] = cell.getId(); + var childCount = this.model.getChildCount(cell); + + for (var j = 0; j < childCount; j++) + { + this.createCellLookup([this.model.getChildAt(cell, j)], lookup); + } + } + + return lookup; + }; + + /** + * Creates lookup from original to cloned cell IDs where mapping is + * the mapping used in cloneCells and lookup is a mapping from + * object IDs to cell IDs. + */ + Graph.prototype.createCellMapping = function(mapping, lookup, cellMapping) + { + cellMapping = (cellMapping != null) ? cellMapping : new Object(); + + for (var objectId in mapping) + { + var cellId = lookup[objectId]; + + if (cellMapping[cellId] == null) + { + // Uses empty string if clone ID was null which means + // the cell was cloned but not inserted into the model. + cellMapping[cellId] = mapping[objectId].getId() || ''; + } + } + + return cellMapping; + }; + + /** + * + */ + Graph.prototype.importGraphModel = function(node, dx, dy, crop) + { + dx = (dx != null) ? dx : 0; + dy = (dy != null) ? dy : 0; + + var codec = new mxCodec(node.ownerDocument); + var tempModel = new mxGraphModel(); + codec.decode(node, tempModel); + var cells = [] + + // Clones cells to remove invalid edges + var cloneMap = new Object(); + var cellMapping = new Object(); + var layers = tempModel.getChildren(this.cloneCell(tempModel.root, + this.isCloneInvalidEdges(), cloneMap)); + + if (layers != null) + { + // Creates lookup from object IDs to cell IDs + var lookup = this.createCellLookup([tempModel.root]); + + // Uses copy as layers are removed from array inside loop + layers = layers.slice(); + + this.model.beginUpdate(); + try + { + // Merges into unlocked current layer if one layer is pasted + if (layers.length == 1 && !this.isCellLocked(this.getDefaultParent())) + { + cells = this.moveCells(tempModel.getChildren(layers[0]), + dx, dy, false, this.getDefaultParent()); + + // Imported default parent maps to local default parent + cellMapping[tempModel.getChildAt(tempModel.root, 0).getId()] = + this.getDefaultParent().getId(); + } + else + { + for (var i = 0; i < layers.length; i++) + { + var children = this.model.getChildren(this.moveCells( + [layers[i]], dx, dy, false, this.model.getRoot())[0]); + + if (children != null) + { + cells = cells.concat(children); + } + } + } + + if (cells != null) + { + // Adds mapping for all cloned entries from imported to local cell ID + this.createCellMapping(cloneMap, lookup, cellMapping); + this.updateCustomLinks(cellMapping, cells); + + if (crop) + { + if (this.isGridEnabled()) + { + dx = this.snap(dx); + dy = this.snap(dy); + } + + var bounds = this.getBoundingBoxFromGeometry(cells, true); + + if (bounds != null) + { + this.moveCells(cells, dx - bounds.x, dy - bounds.y); + } + } + } + } + finally + { + this.model.endUpdate(); + } + } + + return cells; + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.encodeCells = function(cells) + { + var cloneMap = new Object(); + var clones = this.cloneCells(cells, null, cloneMap); + + // Creates a dictionary for fast lookups + var dict = new mxDictionary(); + + for (var i = 0; i < cells.length; i++) + { + dict.put(cells[i], true); + } + + // Checks for orphaned relative children and makes absolute + for (var i = 0; i < clones.length; i++) + { + var state = this.view.getState(cells[i]); + + if (state != null) + { + var geo = this.getCellGeometry(clones[i]); + + if (geo != null && geo.relative && !this.model.isEdge(cells[i]) && + !dict.get(this.model.getParent(cells[i]))) + { + geo.relative = false; + geo.x = state.x / state.view.scale - state.view.translate.x; + geo.y = state.y / state.view.scale - state.view.translate.y; + } + } + } + + var codec = new mxCodec(); + var model = new mxGraphModel(); + var parent = model.getChildAt(model.getRoot(), 0); + + for (var i = 0; i < clones.length; i++) + { + model.add(parent, clones[i]); + } + + this.updateCustomLinks(this.createCellMapping(cloneMap, + this.createCellLookup(cells)), clones); + + return codec.encode(model); + }; + + /** + * Overrides cloning cells in moveCells. + */ + var graphMoveCells = Graph.prototype.moveCells; + + Graph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping) + { + mapping = (mapping != null) ? mapping : new Object(); + var result = graphMoveCells.apply(this, arguments); + + if (clone) + { + this.updateCustomLinks(this.createCellMapping(mapping, + this.createCellLookup(cells)), result); + } + + return result; + }; + + /** + * Updates cells IDs for custom links in the given cells. + */ + Graph.prototype.updateCustomLinks = function(mapping, cells) + { + for (var i = 0; i < cells.length; i++) + { + if (cells[i] != null) + { + this.updateCustomLinksForCell(mapping, cells[i]); + } + } + }; + + /** + * Updates cell IDs in custom links on the given cell and its label. + */ + Graph.prototype.updateCustomLinksForCell = function(mapping, cell) + { + // Hook for subclassers + }; + + /** + * Overrides method to provide connection constraints for shapes. + */ + Graph.prototype.getAllConnectionConstraints = function(terminal, source) + { + if (terminal != null) + { + var constraints = mxUtils.getValue(terminal.style, 'points', null); + + if (constraints != null) + { + // Requires an array of arrays with x, y (0..1), an optional + // [perimeter (0 or 1), dx, and dy] eg. points=[[0,0,1,-10,10],[0,1,0],[1,1]] + var result = []; + + try + { + var c = JSON.parse(constraints); + + for (var i = 0; i < c.length; i++) + { + var tmp = c[i]; + result.push(new mxConnectionConstraint(new mxPoint(tmp[0], tmp[1]), (tmp.length > 2) ? tmp[2] != '0' : true, + null, (tmp.length > 3) ? tmp[3] : 0, (tmp.length > 4) ? tmp[4] : 0)); + } + } + catch (e) + { + // ignore + } + + return result; + } + else if (terminal.shape != null && terminal.shape.bounds != null) + { + var dir = terminal.shape.direction; + var bounds = terminal.shape.bounds; + var scale = terminal.shape.scale; + var w = bounds.width / scale; + var h = bounds.height / scale; + + if (dir == mxConstants.DIRECTION_NORTH || dir == mxConstants.DIRECTION_SOUTH) + { + var tmp = w; + w = h; + h = tmp; + } + + constraints = terminal.shape.getConstraints(terminal.style, w, h); + + if (constraints != null) + { + return constraints; + } + else if (terminal.shape.stencil != null && terminal.shape.stencil.constraints != null) + { + return terminal.shape.stencil.constraints; + } + else if (terminal.shape.constraints != null) + { + return terminal.shape.constraints; + } + } + } + + return null; + }; + + /** + * Inverts the elbow edge style without removing existing styles. + */ + Graph.prototype.flipEdge = function(edge) + { + if (edge != null) + { + var style = this.getCurrentCellStyle(edge); + var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW, + mxConstants.ELBOW_HORIZONTAL); + var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ? + mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL; + this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]); + } + }; + + /** + * Disables drill-down for non-swimlanes. + */ + Graph.prototype.isValidRoot = function(cell) + { + // Counts non-relative children + var childCount = this.model.getChildCount(cell); + var realChildCount = 0; + + for (var i = 0; i < childCount; i++) + { + var child = this.model.getChildAt(cell, i); + + if (this.model.isVertex(child)) + { + var geometry = this.getCellGeometry(child); + + if (geometry != null && !geometry.relative) + { + realChildCount++; + } + } + } + + return realChildCount > 0 || this.isContainer(cell); + }; + + /** + * Disables drill-down for non-swimlanes. + */ + Graph.prototype.isValidDropTarget = function(cell) + { + var style = this.getCurrentCellStyle(cell); + + return (mxUtils.getValue(style, 'part', '0') != '1' || + this.isContainer(cell)) && + mxUtils.getValue(style, 'dropTarget', '1') != '0' && + (mxGraph.prototype.isValidDropTarget.apply(this, arguments) || + this.isContainer(cell)); + }; + + /** + * Overrides createGroupCell to set the group style for new groups to 'group'. + */ + Graph.prototype.createGroupCell = function() + { + var group = mxGraph.prototype.createGroupCell.apply(this, arguments); + group.setStyle('group'); + + return group; + }; + + /** + * Disables extending parents with stack layouts on add + */ + Graph.prototype.isExtendParentsOnAdd = function(cell) + { + var result = mxGraph.prototype.isExtendParentsOnAdd.apply(this, arguments); + + if (result && cell != null && this.layoutManager != null) + { + var parent = this.model.getParent(cell); + + if (parent != null) + { + var layout = this.layoutManager.getLayout(parent); + + if (layout != null && layout.constructor == mxStackLayout) + { + result = false; + } + } + } + + return result; + }; + + /** + * Overrides autosize to add a border. + */ + Graph.prototype.getPreferredSizeForCell = function(cell) + { + var result = mxGraph.prototype.getPreferredSizeForCell.apply(this, arguments); + + // Adds buffer + if (result != null) + { + result.width += 10; + result.height += 4; + + if (this.gridEnabled) + { + result.width = this.snap(result.width); + result.height = this.snap(result.height); + } + } + + return result; + } + + /** + * Turns the given cells and returns the changed cells. + */ + Graph.prototype.turnShapes = function(cells, backwards) + { + var model = this.getModel(); + var select = []; + + model.beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (model.isEdge(cell)) + { + var src = model.getTerminal(cell, true); + var trg = model.getTerminal(cell, false); + + model.setTerminal(cell, trg, true); + model.setTerminal(cell, src, false); + + var geo = model.getGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + + if (geo.points != null) + { + geo.points.reverse(); + } + + var sp = geo.getTerminalPoint(true); + var tp = geo.getTerminalPoint(false) + + geo.setTerminalPoint(sp, false); + geo.setTerminalPoint(tp, true); + model.setGeometry(cell, geo); + + // Inverts constraints + var edgeState = this.view.getState(cell); + var sourceState = this.view.getState(src); + var targetState = this.view.getState(trg); + + if (edgeState != null) + { + var sc = (sourceState != null) ? this.getConnectionConstraint(edgeState, sourceState, true) : null; + var tc = (targetState != null) ? this.getConnectionConstraint(edgeState, targetState, false) : null; + + this.setConnectionConstraint(cell, src, true, tc); + this.setConnectionConstraint(cell, trg, false, sc); + } + + select.push(cell); + } + } + else if (model.isVertex(cell)) + { + var geo = this.getCellGeometry(cell); + + if (geo != null) + { + // Rotates the size and position in the geometry + geo = geo.clone(); + geo.x += geo.width / 2 - geo.height / 2; + geo.y += geo.height / 2 - geo.width / 2; + var tmp = geo.width; + geo.width = geo.height; + geo.height = tmp; + model.setGeometry(cell, geo); + + // Reads the current direction and advances by 90 degrees + var state = this.view.getState(cell); + + if (state != null) + { + var dirs = [mxConstants.DIRECTION_EAST, mxConstants.DIRECTION_SOUTH, + mxConstants.DIRECTION_WEST, mxConstants.DIRECTION_NORTH]; + var dir = mxUtils.getValue(state.style, mxConstants.STYLE_DIRECTION, + mxConstants.DIRECTION_EAST); + this.setCellStyles(mxConstants.STYLE_DIRECTION, + dirs[mxUtils.mod(mxUtils.indexOf(dirs, dir) + + ((backwards) ? -1 : 1), dirs.length)], [cell]); + } + + select.push(cell); + } + } + } + } + finally + { + model.endUpdate(); + } + + return select; + }; + + /** + * Returns true if the given stencil contains any placeholder text. + */ + Graph.prototype.stencilHasPlaceholders = function(stencil) + { + if (stencil != null && stencil.fgNode != null) + { + var node = stencil.fgNode.firstChild; + + while (node != null) + { + if (node.nodeName == 'text' && node.getAttribute('placeholders') == '1') + { + return true; + } + + node = node.nextSibling; + } + } + + return false; + }; + + /** + * Updates the child cells with placeholders if metadata of a cell has changed. + */ + Graph.prototype.processChange = function(change) + { + mxGraph.prototype.processChange.apply(this, arguments); + + if (change instanceof mxValueChange && change.cell != null && + change.cell.value != null && typeof(change.cell.value) == 'object') + { + this.invalidateDescendantsWithPlaceholders(change.cell); + } + }; + + /** + * Replaces the given element with a span. + */ + Graph.prototype.invalidateDescendantsWithPlaceholders = function(cell) + { + // Invalidates all descendants with placeholders + var desc = this.model.getDescendants(cell); + + // LATER: Check if only label or tooltip have changed + if (desc.length > 0) + { + for (var i = 0; i < desc.length; i++) + { + var state = this.view.getState(desc[i]); + + if (state != null && state.shape != null && state.shape.stencil != null && + this.stencilHasPlaceholders(state.shape.stencil)) + { + this.removeStateForCell(desc[i]); + } + else if (this.isReplacePlaceholders(desc[i])) + { + this.view.invalidate(desc[i], false, false); + } + } + } + }; + + /** + * Replaces the given element with a span. + */ + Graph.prototype.replaceElement = function(elt, tagName) + { + var span = elt.ownerDocument.createElement((tagName != null) ? tagName : 'span'); + var attributes = Array.prototype.slice.call(elt.attributes); + + while (attr = attributes.pop()) + { + span.setAttribute(attr.nodeName, attr.nodeValue); + } + + span.innerHTML = elt.innerHTML; + elt.parentNode.replaceChild(span, elt); + }; + + /** + * + */ + Graph.prototype.processElements = function(elt, fn) + { + if (elt != null) + { + var elts = elt.getElementsByTagName('*'); + + for (var i = 0; i < elts.length; i++) + { + fn(elts[i]); + } + } + }; + + /** + * Handles label changes for XML user objects. + */ + Graph.prototype.updateLabelElements = function(cells, fn, tagName) + { + cells = (cells != null) ? cells : this.getSelectionCells(); + var div = document.createElement('div'); + + for (var i = 0; i < cells.length; i++) + { + // Changes font tags inside HTML labels + if (this.isHtmlLabel(cells[i])) + { + var label = this.convertValueToString(cells[i]); + + if (label != null && label.length > 0) + { + div.innerHTML = label; + var elts = div.getElementsByTagName((tagName != null) ? tagName : '*'); + + for (var j = 0; j < elts.length; j++) + { + fn(elts[j]); + } + + if (div.innerHTML != label) + { + this.cellLabelChanged(cells[i], div.innerHTML); + } + } + } + } + }; + + /** + * Handles label changes for XML user objects. + */ + Graph.prototype.cellLabelChanged = function(cell, value, autoSize) + { + // Removes all illegal control characters in user input + value = Graph.zapGremlins(value); + + this.model.beginUpdate(); + try + { + if (cell.value != null && typeof cell.value == 'object') + { + if (this.isReplacePlaceholders(cell) && + cell.getAttribute('placeholder') != null) + { + // LATER: Handle delete, name change + var name = cell.getAttribute('placeholder'); + var current = cell; + + while (current != null) + { + if (current == this.model.getRoot() || (current.value != null && + typeof(current.value) == 'object' && current.hasAttribute(name))) + { + this.setAttributeForCell(current, name, value); + + break; + } + + current = this.model.getParent(current); + } + } + + var tmp = cell.value.cloneNode(true); + tmp.setAttribute('label', value); + value = tmp; + } + + mxGraph.prototype.cellLabelChanged.apply(this, arguments); + } + finally + { + this.model.endUpdate(); + } + }; + + /** + * Removes transparent empty groups if all children are removed. + */ + Graph.prototype.cellsRemoved = function(cells) + { + if (cells != null) + { + var dict = new mxDictionary(); + + for (var i = 0; i < cells.length; i++) + { + dict.put(cells[i], true); + } + + // LATER: Recurse up the cell hierarchy + var parents = []; + + for (var i = 0; i < cells.length; i++) + { + var parent = this.model.getParent(cells[i]); + + if (parent != null && !dict.get(parent)) + { + dict.put(parent, true); + parents.push(parent); + } + } + + for (var i = 0; i < parents.length; i++) + { + var state = this.view.getState(parents[i]); + + if (state != null && (this.model.isEdge(state.cell) || + this.model.isVertex(state.cell)) && + this.isCellDeletable(state.cell) && + this.isTransparentState(state)) + { + var allChildren = true; + + for (var j = 0; j < this.model.getChildCount(state.cell) && allChildren; j++) + { + if (!dict.get(this.model.getChildAt(state.cell, j))) + { + allChildren = false; + } + } + + if (allChildren) + { + cells.push(state.cell); + } + } + } + } + + mxGraph.prototype.cellsRemoved.apply(this, arguments); + }; + + /** + * Overrides ungroup to check if group should be removed. + */ + Graph.prototype.removeCellsAfterUngroup = function(cells) + { + var cellsToRemove = []; + + for (var i = 0; i < cells.length; i++) + { + if (this.isCellDeletable(cells[i]) && this.isTransparentState( + this.view.getState(cells[i]))) + { + cellsToRemove.push(cells[i]); + } + } + + cells = cellsToRemove; + + mxGraph.prototype.removeCellsAfterUngroup.apply(this, arguments); + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setLinkForCell = function(cell, link) + { + this.setAttributeForCell(cell, 'link', link); + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setTooltipForCell = function(cell, link) + { + this.setAttributeForCell(cell, 'tooltip', link); + }; + + /** + * Returns the cells in the model (or given array) that have all of the + * given tags in their tags property. + */ + Graph.prototype.getAttributeForCell = function(cell, attributeName, defaultValue) + { + var value = (cell.value != null && typeof cell.value === 'object') ? + cell.value.getAttribute(attributeName) : null; + + return (value != null) ? value : defaultValue; + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setAttributeForCell = function(cell, attributeName, attributeValue) + { + var value = null; + + if (cell.value != null && typeof(cell.value) == 'object') + { + value = cell.value.cloneNode(true); + } + else + { + var doc = mxUtils.createXmlDocument(); + + value = doc.createElement('UserObject'); + value.setAttribute('label', cell.value || ''); + } + + if (attributeValue != null) + { + value.setAttribute(attributeName, attributeValue); + } + else + { + value.removeAttribute(attributeName); + } + + this.model.setValue(cell, value); + }; + + /** + * Overridden to stop moving edge labels between cells. + */ + Graph.prototype.getDropTarget = function(cells, evt, cell, clone) + { + var model = this.getModel(); + + // Disables drop into group if alt is pressed + if (mxEvent.isAltDown(evt)) + { + return null; + } + + // Disables dragging edge labels out of edges + for (var i = 0; i < cells.length; i++) + { + if (this.model.isEdge(this.model.getParent(cells[i]))) + { + return null; + } + } + + return mxGraph.prototype.getDropTarget.apply(this, arguments); + }; + + /** + * Overrides double click handling to avoid accidental inserts of new labels in dblClick below. + */ + Graph.prototype.click = function(me) + { + mxGraph.prototype.click.call(this, me); + + // Stores state and source for checking in dblClick + this.firstClickState = me.getState(); + this.firstClickSource = me.getSource(); + }; + + /** + * Overrides double click handling to add the tolerance and inserting text. + */ + Graph.prototype.dblClick = function(evt, cell) + { + if (this.isEnabled()) + { + var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + + // Automatically adds new child cells to edges on double click + if (evt != null && !this.model.isVertex(cell)) + { + var state = (this.model.isEdge(cell)) ? this.view.getState(cell) : null; + var src = mxEvent.getSource(evt); + + if ((this.firstClickState == state && this.firstClickSource == src) && + (state == null || (state.text == null || state.text.node == null || + state.text.boundingBox == null || (!mxUtils.contains(state.text.boundingBox, + pt.x, pt.y) && !mxUtils.isAncestorNode(state.text.node, mxEvent.getSource(evt))))) && + ((state == null && !this.isCellLocked(this.getDefaultParent())) || + (state != null && !this.isCellLocked(state.cell))) && + (state != null || (mxClient.IS_VML && src == this.view.getCanvas()) || + (mxClient.IS_SVG && src == this.view.getCanvas().ownerSVGElement))) + { + if (state == null) + { + state = this.view.getState(this.getCellAt(pt.x, pt.y)); + } + + cell = this.addText(pt.x, pt.y, state); + } + } + + mxGraph.prototype.dblClick.call(this, evt, cell); + } + }; + + /** + * Returns a point that specifies the location for inserting cells. + */ + Graph.prototype.getInsertPoint = function() + { + var gs = this.getGridSize(); + var dx = this.container.scrollLeft / this.view.scale - this.view.translate.x; + var dy = this.container.scrollTop / this.view.scale - this.view.translate.y; + + if (this.pageVisible) + { + var layout = this.getPageLayout(); + var page = this.getPageSize(); + dx = Math.max(dx, layout.x * page.width); + dy = Math.max(dy, layout.y * page.height); + } + + return new mxPoint(this.snap(dx + gs), this.snap(dy + gs)); + }; + + /** + * + */ + Graph.prototype.getFreeInsertPoint = function() + { + var view = this.view; + var bds = this.getGraphBounds(); + var pt = this.getInsertPoint(); + + // Places at same x-coord and 2 grid sizes below existing graph + var x = this.snap(Math.round(Math.max(pt.x, bds.x / view.scale - view.translate.x + + ((bds.width == 0) ? 2 * this.gridSize : 0)))); + var y = this.snap(Math.round(Math.max(pt.y, (bds.y + bds.height) / view.scale - view.translate.y + + 2 * this.gridSize))); + + return new mxPoint(x, y); + }; + + /** + * + */ + Graph.prototype.getCenterInsertPoint = function(bbox) + { + bbox = (bbox != null) ? bbox : new mxRectangle(); + + if (mxUtils.hasScrollbars(this.container)) + { + return new mxPoint( + this.snap((this.container.scrollLeft + this.container.clientWidth / 2) / this.view.scale - + this.view.translate.x - bbox.width / 2), + this.snap((this.container.scrollTop + this.container.clientHeight / 2) / this.view.scale - + this.view.translate.y - bbox.height / 2)); + } + else + { + return new mxPoint( + this.snap(this.container.clientWidth / 2 / this.view.scale - + this.view.translate.x - bbox.width / 2), + this.snap(this.container.clientHeight / 2 / this.view.scale - + this.view.translate.y - bbox.height / 2)); + } + }; + + /** + * Hook for subclassers to return true if the current insert point was defined + * using a mouse hover event. + */ + Graph.prototype.isMouseInsertPoint = function() + { + return false; + }; + + /** + * Adds a new label at the given position and returns the new cell. State is + * an optional edge state to be used as the parent for the label. Vertices + * are not allowed currently as states. + */ + Graph.prototype.addText = function(x, y, state) + { + // Creates a new edge label with a predefined text + var label = new mxCell(); + label.value = 'Text'; + label.style = 'text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];' + label.geometry = new mxGeometry(0, 0, 0, 0); + label.vertex = true; + + if (state != null && this.model.isEdge(state.cell)) + { + label.style += 'labelBackgroundColor=#ffffff;' + label.geometry.relative = true; + label.connectable = false; + + // Resets the relative location stored inside the geometry + var pt2 = this.view.getRelativePoint(state, x, y); + label.geometry.x = Math.round(pt2.x * 10000) / 10000; + label.geometry.y = Math.round(pt2.y); + + // Resets the offset inside the geometry to find the offset from the resulting point + label.geometry.offset = new mxPoint(0, 0); + pt2 = this.view.getPoint(state, label.geometry); + + var scale = this.view.scale; + label.geometry.offset = new mxPoint(Math.round((x - pt2.x) / scale), Math.round((y - pt2.y) / scale)); + } + else + { + var tr = this.view.translate; + label.geometry.width = 40; + label.geometry.height = 20; + label.geometry.x = Math.round(x / this.view.scale) - + tr.x - ((state != null) ? state.origin.x : 0); + label.geometry.y = Math.round(y / this.view.scale) - + tr.y - ((state != null) ? state.origin.y : 0); + label.style += 'autosize=1;' + } + + this.getModel().beginUpdate(); + try + { + this.addCells([label], (state != null) ? state.cell : null); + this.fireEvent(new mxEventObject('textInserted', 'cells', [label])); + + // Updates size of text after possible change of style via event + this.autoSizeCell(label); + } + finally + { + this.getModel().endUpdate(); + } + + return label; + }; + + /** + * Adds a handler for clicking on shapes with links. This replaces all links in labels. + */ + Graph.prototype.addClickHandler = function(highlight, beforeClick, onClick) + { + // Replaces links in labels for consistent right-clicks + var checkLinks = mxUtils.bind(this, function() + { + var links = this.container.getElementsByTagName('a'); + + if (links != null) + { + for (var i = 0; i < links.length; i++) + { + var href = this.getAbsoluteUrl(links[i].getAttribute('href')); + + if (href != null) + { + links[i].setAttribute('rel', this.linkRelation); + links[i].setAttribute('href', href); + + if (beforeClick != null) + { + mxEvent.addGestureListeners(links[i], null, null, beforeClick); + } + } + } + } + }); + + this.model.addListener(mxEvent.CHANGE, checkLinks); + checkLinks(); + + var cursor = this.container.style.cursor; + var tol = this.getTolerance(); + var graph = this; + + var mouseListener = + { + currentState: null, + currentLink: null, + highlight: (highlight != null && highlight != '' && highlight != mxConstants.NONE) ? + new mxCellHighlight(graph, highlight, 4) : null, + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0, + updateCurrentState: function(me) + { + var tmp = me.sourceState; + + // Gets topmost intersecting cell with link + if (tmp == null || graph.getLinkForCell(tmp.cell) == null) + { + var cell = graph.getCellAt(me.getGraphX(), me.getGraphY(), null, null, null, function(state, x, y) + { + return graph.getLinkForCell(state.cell) == null; + }); + + tmp = graph.view.getState(cell); + } + + if (tmp != this.currentState) + { + if (this.currentState != null) + { + this.clear(); + } + + this.currentState = tmp; + + if (this.currentState != null) + { + this.activate(this.currentState); + } + } + }, + mouseDown: function(sender, me) + { + this.startX = me.getGraphX(); + this.startY = me.getGraphY(); + this.scrollLeft = graph.container.scrollLeft; + this.scrollTop = graph.container.scrollTop; + + if (this.currentLink == null && graph.container.style.overflow == 'auto') + { + graph.container.style.cursor = 'move'; + } + + this.updateCurrentState(me); + }, + mouseMove: function(sender, me) + { + if (graph.isMouseDown) + { + if (this.currentLink != null) + { + var dx = Math.abs(this.startX - me.getGraphX()); + var dy = Math.abs(this.startY - me.getGraphY()); + + if (dx > tol || dy > tol) + { + this.clear(); + } + } + } + else + { + // Checks for parent link + var linkNode = me.getSource(); + + while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') + { + linkNode = linkNode.parentNode; + } + + if (linkNode != null) + { + this.clear(); + } + else + { + if (graph.tooltipHandler != null && this.currentLink != null && this.currentState != null) + { + graph.tooltipHandler.reset(me, true, this.currentState); + } + + if (this.currentState != null && (me.getState() == this.currentState || me.sourceState == null) && + graph.intersects(this.currentState, me.getGraphX(), me.getGraphY())) + { + return; + } + + this.updateCurrentState(me); + } + } + }, + mouseUp: function(sender, me) + { + var source = me.getSource(); + var evt = me.getEvent(); + + // Checks for parent link + var linkNode = source; + + while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') + { + linkNode = linkNode.parentNode; + } + + // Ignores clicks on links and collapse/expand icon + if (linkNode == null && + (((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (me.sourceState == null || !me.isSource(me.sourceState.control))) && + (((mxEvent.isLeftMouseButton(evt) || mxEvent.isMiddleMouseButton(evt)) && + !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)))) + { + if (this.currentLink != null) + { + var blank = graph.isBlankLink(this.currentLink); + + if ((this.currentLink.substring(0, 5) === 'data:' || + !blank) && beforeClick != null) + { + beforeClick(evt, this.currentLink); + } + + if (!mxEvent.isConsumed(evt)) + { + var target = (mxEvent.isMiddleMouseButton(evt)) ? '_blank' : + ((blank) ? graph.linkTarget : '_top'); + graph.openLink(this.currentLink, target); + me.consume(); + } + } + else if (onClick != null && !me.isConsumed() && + (Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (Math.abs(this.startX - me.getGraphX()) < tol && + Math.abs(this.startY - me.getGraphY()) < tol)) + { + onClick(me.getEvent()); + } + } + + this.clear(); + }, + activate: function(state) + { + this.currentLink = graph.getAbsoluteUrl(graph.getLinkForCell(state.cell)); + + if (this.currentLink != null) + { + graph.container.style.cursor = 'pointer'; + + if (this.highlight != null) + { + this.highlight.highlight(state); + } + } + }, + clear: function() + { + if (graph.container != null) + { + graph.container.style.cursor = cursor; + } + + this.currentState = null; + this.currentLink = null; + + if (this.highlight != null) + { + this.highlight.hide(); + } + + if (graph.tooltipHandler != null) + { + graph.tooltipHandler.hide(); + } + } + }; + + // Ignores built-in click handling + graph.click = function(me) {}; + graph.addMouseListener(mouseListener); + + mxEvent.addListener(document, 'mouseleave', function(evt) + { + mouseListener.clear(); + }); + }; + + /** + * Duplicates the given cells and returns the duplicates. + */ + Graph.prototype.duplicateCells = function(cells, append) + { + cells = (cells != null) ? cells : this.getSelectionCells(); + append = (append != null) ? append : true; + + cells = this.model.getTopmostCells(cells); + + var model = this.getModel(); + var s = this.gridSize; + var select = []; + + model.beginUpdate(); + try + { + var clones = this.cloneCells(cells, false, null, true); + + for (var i = 0; i < cells.length; i++) + { + var parent = model.getParent(cells[i]); + var child = this.moveCells([clones[i]], s, s, false)[0]; + select.push(child); + + if (append) + { + model.add(parent, clones[i]); + } + else + { + // Maintains child index by inserting after clone in parent + var index = parent.getIndex(cells[i]); + model.add(parent, clones[i], index + 1); + } + } + } + finally + { + model.endUpdate(); + } + + return select; + }; + + /** + * Inserts the given image at the cursor in a content editable text box using + * the insertimage command on the document instance. + */ + Graph.prototype.insertImage = function(newValue, w, h) + { + // To find the new image, we create a list of all existing links first + if (newValue != null && this.cellEditor.textarea != null) + { + var tmp = this.cellEditor.textarea.getElementsByTagName('img'); + var oldImages = []; + + for (var i = 0; i < tmp.length; i++) + { + oldImages.push(tmp[i]); + } + + // LATER: Fix inserting link/image in IE8/quirks after focus lost + document.execCommand('insertimage', false, newValue); + + // Sets size of new image + var newImages = this.cellEditor.textarea.getElementsByTagName('img'); + + if (newImages.length == oldImages.length + 1) + { + // Inverse order in favor of appended images + for (var i = newImages.length - 1; i >= 0; i--) + { + if (i == 0 || newImages[i] != oldImages[i - 1]) + { + // Workaround for lost styles during undo and redo is using attributes + newImages[i].setAttribute('width', w); + newImages[i].setAttribute('height', h); + + break; + } + } + } + } + }; + + /** + * Inserts the given image at the cursor in a content editable text box using + * the insertimage command on the document instance. + */ + Graph.prototype.insertLink = function(value) + { + if (this.cellEditor.textarea != null) + { + if (value.length == 0) + { + document.execCommand('unlink', false); + } + else if (mxClient.IS_FF) + { + // Workaround for Firefox that adds a new link and removes + // the href from the inner link if its parent is a span is + // to remove all inner links inside the new outer link + var tmp = this.cellEditor.textarea.getElementsByTagName('a'); + var oldLinks = []; + + for (var i = 0; i < tmp.length; i++) + { + oldLinks.push(tmp[i]); + } + + document.execCommand('createlink', false, mxUtils.trim(value)); + + // Finds the new link element + var newLinks = this.cellEditor.textarea.getElementsByTagName('a'); + + if (newLinks.length == oldLinks.length + 1) + { + // Inverse order in favor of appended links + for (var i = newLinks.length - 1; i >= 0; i--) + { + if (newLinks[i] != oldLinks[i - 1]) + { + // Removes all inner links from the new link and + // moves the children to the inner link parent + var tmp = newLinks[i].getElementsByTagName('a'); + + while (tmp.length > 0) + { + var parent = tmp[0].parentNode; + + while (tmp[0].firstChild != null) + { + parent.insertBefore(tmp[0].firstChild, tmp[0]); + } + + parent.removeChild(tmp[0]); + } + + break; + } + } + } + } + else + { + // LATER: Fix inserting link/image in IE8/quirks after focus lost + document.execCommand('createlink', false, mxUtils.trim(value)); + } + } + }; + + /** + * + * @param cell + * @returns {Boolean} + */ + Graph.prototype.isCellResizable = function(cell) + { + var result = mxGraph.prototype.isCellResizable.apply(this, arguments); + var style = this.getCurrentCellStyle(cell); + + return result || (mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0' && + style[mxConstants.STYLE_WHITE_SPACE] == 'wrap'); + }; + + /** + * Function: distributeCells + * + * Distribuets the centers of the given cells equally along the available + * horizontal or vertical space. + * + * Parameters: + * + * horizontal - Boolean that specifies the direction of the distribution. + * cells - Optional array of to be distributed. Edges are ignored. + */ + Graph.prototype.distributeCells = function(horizontal, cells) + { + if (cells == null) + { + cells = this.getSelectionCells(); + } + + if (cells != null && cells.length > 1) + { + var vertices = []; + var max = null; + var min = null; + + for (var i = 0; i < cells.length; i++) + { + if (this.getModel().isVertex(cells[i])) + { + var state = this.view.getState(cells[i]); + + if (state != null) + { + var tmp = (horizontal) ? state.getCenterX() : state.getCenterY(); + max = (max != null) ? Math.max(max, tmp) : tmp; + min = (min != null) ? Math.min(min, tmp) : tmp; + + vertices.push(state); + } + } + } + + if (vertices.length > 2) + { + vertices.sort(function(a, b) + { + return (horizontal) ? a.x - b.x : a.y - b.y; + }); + + var t = this.view.translate; + var s = this.view.scale; + + min = min / s - ((horizontal) ? t.x : t.y); + max = max / s - ((horizontal) ? t.x : t.y); + + this.getModel().beginUpdate(); + try + { + var dt = (max - min) / (vertices.length - 1); + var t0 = min; + + for (var i = 1; i < vertices.length - 1; i++) + { + var pstate = this.view.getState(this.model.getParent(vertices[i].cell)); + var geo = this.getCellGeometry(vertices[i].cell); + t0 += dt; + + if (geo != null && pstate != null) + { + geo = geo.clone(); + + if (horizontal) + { + geo.x = Math.round(t0 - geo.width / 2) - pstate.origin.x; + } + else + { + geo.y = Math.round(t0 - geo.height / 2) - pstate.origin.y; + } + + this.getModel().setGeometry(vertices[i].cell, geo); + } + } + } + finally + { + this.getModel().endUpdate(); + } + } + } + + return cells; + }; + + /** + * Adds meta-drag an Mac. + * @param evt + * @returns + */ + Graph.prototype.isCloneEvent = function(evt) + { + return (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || mxEvent.isControlDown(evt); + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.createSvgImageExport = function() + { + var exp = new mxImageExport(); + + // Adds hyperlinks (experimental) + exp.getLinkForCellState = mxUtils.bind(this, function(state, canvas) + { + return this.getLinkForCell(state.cell); + }); + + return exp; + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp, + ignoreSelection, showText, imgExport, linkTarget, hasShadow) + { + //Disable Css Transforms if it is used + var origUseCssTrans = this.useCssTransforms; + + if (origUseCssTrans) + { + this.useCssTransforms = false; + this.view.revalidate(); + this.sizeDidChange(); + } + + try + { + scale = (scale != null) ? scale : 1; + border = (border != null) ? border : 0; + crisp = (crisp != null) ? crisp : true; + ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; + showText = (showText != null) ? showText : true; + + var bounds = (ignoreSelection || nocrop) ? + this.getGraphBounds() : this.getBoundingBox( + this.getSelectionCells()); + + if (bounds == null) + { + throw Error(mxResources.get('drawingEmpty')); + } + + var vs = this.view.scale; + + // Prepares SVG document that holds the output + var svgDoc = mxUtils.createXmlDocument(); + var root = (svgDoc.createElementNS != null) ? + svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg'); + + if (background != null) + { + if (root.style != null) + { + root.style.backgroundColor = background; + } + else + { + root.setAttribute('style', 'background-color:' + background); + } + } + + if (svgDoc.createElementNS == null) + { + root.setAttribute('xmlns', mxConstants.NS_SVG); + root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK); + } + else + { + // KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround. + root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK); + } + + var s = scale / vs; + var w = Math.max(1, Math.ceil(bounds.width * s) + 2 * border) + ((hasShadow) ? 5 : 0); + var h = Math.max(1, Math.ceil(bounds.height * s) + 2 * border) + ((hasShadow) ? 5 : 0); + + root.setAttribute('version', '1.1'); + root.setAttribute('width', w + 'px'); + root.setAttribute('height', h + 'px'); + root.setAttribute('viewBox', ((crisp) ? '-0.5 -0.5' : '0 0') + ' ' + w + ' ' + h); + svgDoc.appendChild(root); + + // Renders graph. Offset will be multiplied with state's scale when painting state. + // TextOffset only seems to affect FF output but used everywhere for consistency. + var group = (svgDoc.createElementNS != null) ? + svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g'); + root.appendChild(group); + + var svgCanvas = this.createSvgCanvas(group); + svgCanvas.foOffset = (crisp) ? -0.5 : 0; + svgCanvas.textOffset = (crisp) ? -0.5 : 0; + svgCanvas.imageOffset = (crisp) ? -0.5 : 0; + svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs), + Math.floor((border / scale - bounds.y) / vs)); + + // Convert HTML entities + var htmlConverter = document.createElement('div'); + + // Adds simple text fallback for viewers with no support for foreignObjects + var getAlternateText = svgCanvas.getAlternateText; + svgCanvas.getAlternateText = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation) + { + // Assumes a max character width of 0.5em + if (str != null && this.state.fontSize > 0) + { + try + { + if (mxUtils.isNode(str)) + { + str = str.innerText; + } + else + { + htmlConverter.innerHTML = str; + str = mxUtils.extractTextWithWhitespace(htmlConverter.childNodes); + } + + // Workaround for substring breaking double byte UTF + var exp = Math.ceil(2 * w / this.state.fontSize); + var result = []; + var length = 0; + var index = 0; + + while ((exp == 0 || length < exp) && index < str.length) + { + var char = str.charCodeAt(index); + + if (char == 10 || char == 13) + { + if (length > 0) + { + break; + } + } + else + { + result.push(str.charAt(index)); + + if (char < 255) + { + length++; + } + } + + index++; + } + + // Uses result and adds ellipsis if more than 1 char remains + if (result.length < str.length && str.length - result.length > 1) + { + str = mxUtils.trim(result.join('')) + '...'; + } + + return str; + } + catch (e) + { + return getAlternateText.apply(this, arguments); + } + } + else + { + return getAlternateText.apply(this, arguments); + } + }; + + // Paints background image + var bgImg = this.backgroundImage; + + if (bgImg != null) + { + var s2 = vs / scale; + var tr = this.view.translate; + var tmp = new mxRectangle(tr.x * s2, tr.y * s2, bgImg.width * s2, bgImg.height * s2); + + // Checks if visible + if (mxUtils.intersects(bounds, tmp)) + { + svgCanvas.image(tr.x, tr.y, bgImg.width, bgImg.height, bgImg.src, true); + } + } + + svgCanvas.scale(s); + svgCanvas.textEnabled = showText; + + imgExport = (imgExport != null) ? imgExport : this.createSvgImageExport(); + var imgExportDrawCellState = imgExport.drawCellState; + + // Ignores custom links + var imgExportGetLinkForCellState = imgExport.getLinkForCellState; + + imgExport.getLinkForCellState = function(state, canvas) + { + var result = imgExportGetLinkForCellState.apply(this, arguments); + + return (result != null && !state.view.graph.isCustomLink(result)) ? result : null; + }; + + // Implements ignoreSelection flag + imgExport.drawCellState = function(state, canvas) + { + var graph = state.view.graph; + var selected = graph.isCellSelected(state.cell); + var parent = graph.model.getParent(state.cell); + + // Checks if parent cell is selected + while (!ignoreSelection && !selected && parent != null) + { + selected = graph.isCellSelected(parent); + parent = graph.model.getParent(parent); + } + + if (ignoreSelection || selected) + { + imgExportDrawCellState.apply(this, arguments); + } + }; + + imgExport.drawState(this.getView().getState(this.model.root), svgCanvas); + this.updateSvgLinks(root, linkTarget, true); + this.addForeignObjectWarning(svgCanvas, root); + + return root; + } + finally + { + if (origUseCssTrans) + { + this.useCssTransforms = true; + this.view.revalidate(); + this.sizeDidChange(); + } + } + }; + + /** + * Adds warning for truncated labels in older viewers. + */ + Graph.prototype.addForeignObjectWarning = function(canvas, root) + { + if (root.getElementsByTagName('foreignObject').length > 0) + { + var sw = canvas.createElement('switch'); + var g1 = canvas.createElement('g'); + g1.setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility'); + var a = canvas.createElement('a'); + a.setAttribute('transform', 'translate(0,-5)'); + + // Workaround for implicit namespace handling in HTML5 export, IE adds NS1 namespace so use code below + // in all IE versions except quirks mode. KNOWN: Adds xlink namespace to each image tag in output. + if (a.setAttributeNS == null || (root.ownerDocument != document && document.documentMode == null)) + { + a.setAttribute('xlink:href', Graph.foreignObjectWarningLink); + a.setAttribute('target', '_blank'); + } + else + { + a.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', Graph.foreignObjectWarningLink); + a.setAttributeNS(mxConstants.NS_XLINK, 'target', '_blank'); + } + + var text = canvas.createElement('text'); + text.setAttribute('text-anchor', 'middle'); + text.setAttribute('font-size', '10px'); + text.setAttribute('x', '50%'); + text.setAttribute('y', '100%'); + mxUtils.write(text, Graph.foreignObjectWarningText); + + sw.appendChild(g1); + a.appendChild(text); + sw.appendChild(a); + root.appendChild(sw); + } + }; + + /** + * Hook for creating the canvas used in getSvg. + */ + Graph.prototype.updateSvgLinks = function(node, target, removeCustom) + { + var links = node.getElementsByTagName('a'); + + for (var i = 0; i < links.length; i++) + { + var href = links[i].getAttribute('href'); + + if (href == null) + { + href = links[i].getAttribute('xlink:href'); + } + + if (href != null) + { + if (target != null && /^https?:\/\//.test(href)) + { + links[i].setAttribute('target', target); + } + else if (removeCustom && this.isCustomLink(href)) + { + links[i].setAttribute('href', 'javascript:void(0);'); + } + } + } + }; + + /** + * Hook for creating the canvas used in getSvg. + */ + Graph.prototype.createSvgCanvas = function(node) + { + var canvas = new mxSvgCanvas2D(node); + + canvas.pointerEvents = true; + + return canvas; + }; + + /** + * Returns the first ancestor of the current selection with the given name. + */ + Graph.prototype.getSelectedElement = function() + { + var node = null; + + if (window.getSelection) + { + var sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var range = sel.getRangeAt(0); + node = range.commonAncestorContainer; + } + } + else if (document.selection) + { + node = document.selection.createRange().parentElement(); + } + + return node; + }; + + /** + * Returns the first ancestor of the current selection with the given name. + */ + Graph.prototype.getParentByName = function(node, name, stopAt) + { + while (node != null) + { + if (node.nodeName == name) + { + return node; + } + + if (node == stopAt) + { + return null; + } + + node = node.parentNode; + } + + return node; + }; + + /** + * Returns the first ancestor of the current selection with the given name. + */ + Graph.prototype.getParentByNames = function(node, names, stopAt) + { + while (node != null) + { + if (mxUtils.indexOf(names, node.nodeName) >= 0) + { + return node; + } + + if (node == stopAt) + { + return null; + } + + node = node.parentNode; + } + + return node; + }; + + /** + * Selects the given node. + */ + Graph.prototype.selectNode = function(node) + { + var sel = null; + + // IE9 and non-IE + if (window.getSelection) + { + sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var range = document.createRange(); + range.selectNode(node); + sel.removeAllRanges(); + sel.addRange(range); + } + } + // IE < 9 + else if ((sel = document.selection) && sel.type != 'Control') + { + var originalRange = sel.createRange(); + originalRange.collapse(true); + var range = sel.createRange(); + range.setEndPoint('StartToStart', originalRange); + range.select(); + } + }; + + /** + * + */ + Graph.prototype.insertTableColumn = function(cell, before) + { + var model = this.getModel(); + model.beginUpdate(); + + try + { + var table = cell; + var index = 0; + + if (this.isTableCell(cell)) + { + var row = model.getParent(cell); + table = model.getParent(row); + index = row.getIndex(cell); + } + else + { + if (this.isTableRow(cell)) + { + table = model.getParent(cell); + } + + if (!before) + { + index = model.getChildCount(model.getChildAt(table, 0)) - 1; + } + } + + for (var i = 0; i < model.getChildCount(table); i++) + { + var row = model.getChildAt(table, i); + var child = model.getChildAt(row, index); + var clone = model.cloneCell(child); + var geo = this.getCellGeometry(clone); + clone.value = null; + + if (geo != null) + { + geo.width = Graph.minTableColumnWidth; + var rowGeo = this.getCellGeometry(row); + + if (rowGeo != null) + { + geo.height = rowGeo.height; + } + } + + model.add(row, clone, index + ((before) ? 0 : 1)); + } + + var tableGeo = this.getCellGeometry(table); + + if (tableGeo != null) + { + tableGeo = tableGeo.clone(); + tableGeo.width += Graph.minTableColumnWidth; + + model.setGeometry(table, tableGeo); + } + } + finally + { + model.endUpdate(); + } + }; + + /** + * + */ + Graph.prototype.insertTableRow = function(cell, before) + { + var model = this.getModel(); + model.beginUpdate(); + + try + { + var table = cell; + var index = 0; + + if (this.isTableCell(cell)) + { + var row = model.getParent(cell); + table = model.getParent(row); + index = table.getIndex(row); + } + else if (this.isTableRow(cell)) + { + table = model.getParent(cell); + index = table.getIndex(cell); + } + else if (!before) + { + index = model.getChildCount(table) - 1; + } + + var row = model.cloneCell(model.getChildAt(table, index)); + row.value = null; + + var rowGeo = this.getCellGeometry(row); + + if (rowGeo != null) + { + rowGeo.height = Graph.minTableRowHeight; + + for (var i = 0; i < model.getChildCount(row); i++) + { + var cell = model.getChildAt(row, i); + cell.value = null; + + var geo = this.getCellGeometry(cell); + + if (geo != null) + { + geo.height = rowGeo.height; + } + } + + model.add(table, row, index + ((before) ? 0 : 1)); + + var tableGeo = this.getCellGeometry(table); + + if (tableGeo != null) + { + tableGeo = tableGeo.clone(); + tableGeo.height += rowGeo.height; + + model.setGeometry(table, tableGeo); + } + } + } + finally + { + model.endUpdate(); + } + }; + + /** + * + */ + Graph.prototype.deleteTableColumn = function(cell) + { + var model = this.getModel(); + model.beginUpdate(); + + try + { + var table = cell; + var index = 0; + + if (this.isTableCell(cell)) + { + var row = model.getParent(cell); + table = model.getParent(row); + index = row.getIndex(cell); + } + else if (this.isTableRow(cell)) + { + table = model.getParent(cell); + index = model.getChildCount(cell) - 1; + } + else if (this.isTable(cell)) + { + index = model.getChildCount(model.getChildAt(cell, 0)) - 1; + } + + var width = 0; + + for (var i = 0; i < model.getChildCount(table); i++) + { + var row = model.getChildAt(table, i); + var child = model.getChildAt(row, index); + model.remove(child); + + var geo = this.getCellGeometry(child); + + if (geo != null) + { + width = Math.max(width, geo.width); + } + } + + var tableGeo = this.getCellGeometry(table); + + if (tableGeo != null) + { + tableGeo = tableGeo.clone(); + tableGeo.width -= width; + + model.setGeometry(table, tableGeo); + } + } + finally + { + model.endUpdate(); + } + }; + + /** + * + */ + Graph.prototype.deleteTableRow = function(cell) + { + var model = this.getModel(); + model.beginUpdate(); + + try + { + var row = cell; + + if (this.isTableCell(cell)) + { + row = model.getParent(cell); + } + else if (this.isTable(cell)) + { + row = model.getChildAt(cell, + model.getChildCount(cell) - 1); + } + + var table = model.getParent(row); + model.remove(row); + var height = 0; + + var geo = this.getCellGeometry(row); + + if (geo != null) + { + height = geo.height; + } + + var tableGeo = this.getCellGeometry(table); + + if (tableGeo != null) + { + tableGeo = tableGeo.clone(); + tableGeo.height -= height; + + model.setGeometry(table, tableGeo); + } + } + finally + { + model.endUpdate(); + } + }; + + /** + * Inserts a new row into the given table. + */ + Graph.prototype.insertRow = function(table, index) + { + var bd = table.tBodies[0]; + var cells = bd.rows[0].cells; + var cols = 0; + + // Counts columns including colspans + for (var i = 0; i < cells.length; i++) + { + var colspan = cells[i].getAttribute('colspan'); + cols += (colspan != null) ? parseInt(colspan) : 1; + } + + var row = bd.insertRow(index); + + for (var i = 0; i < cols; i++) + { + mxUtils.br(row.insertCell(-1)); + } + + return row.cells[0]; + }; + + /** + * Deletes the given column. + */ + Graph.prototype.deleteRow = function(table, index) + { + table.tBodies[0].deleteRow(index); + }; + + /** + * Deletes the given column. + */ + Graph.prototype.insertColumn = function(table, index) + { + var hd = table.tHead; + + if (hd != null) + { + // TODO: use colIndex + for (var h = 0; h < hd.rows.length; h++) + { + var th = document.createElement('th'); + hd.rows[h].appendChild(th); + mxUtils.br(th); + } + } + + var bd = table.tBodies[0]; + + for (var i = 0; i < bd.rows.length; i++) + { + var cell = bd.rows[i].insertCell(index); + mxUtils.br(cell); + } + + return bd.rows[0].cells[(index >= 0) ? index : bd.rows[0].cells.length - 1]; + }; + + /** + * Deletes the given column. + */ + Graph.prototype.deleteColumn = function(table, index) + { + if (index >= 0) + { + var bd = table.tBodies[0]; + var rows = bd.rows; + + for (var i = 0; i < rows.length; i++) + { + if (rows[i].cells.length > index) + { + rows[i].deleteCell(index); + } + } + } + }; + + /** + * Inserts the given HTML at the caret position (no undo). + */ + Graph.prototype.pasteHtmlAtCaret = function(html) + { + var sel, range; + + // IE9 and non-IE + if (window.getSelection) + { + sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + range = sel.getRangeAt(0); + range.deleteContents(); + + // Range.createContextualFragment() would be useful here but is + // only relatively recently standardized and is not supported in + // some browsers (IE9, for one) + var el = document.createElement("div"); + el.innerHTML = html; + var frag = document.createDocumentFragment(), node; + + while ((node = el.firstChild)) + { + lastNode = frag.appendChild(node); + } + + range.insertNode(frag); + } + } + // IE < 9 + else if ((sel = document.selection) && sel.type != "Control") + { + // FIXME: Does not work if selection is empty + sel.createRange().pasteHTML(html); + } + }; + + /** + * Creates an anchor elements for handling the given link in the + * hint that is shown when the cell is selected. + */ + Graph.prototype.createLinkForHint = function(link, label) + { + link = (link != null) ? link : 'javascript:void(0);'; + + if (label == null || label.length == 0) + { + if (this.isCustomLink(link)) + { + label = this.getLinkTitle(link); + } + else + { + label = link; + } + } + + // Helper function to shorten strings + function short(str, max) + { + if (str.length > max) + { + str = str.substring(0, Math.round(max / 2)) + '...' + + str.substring(str.length - Math.round(max / 4)); + } + + return str; + }; + + var a = document.createElement('a'); + a.setAttribute('rel', this.linkRelation); + a.setAttribute('href', this.getAbsoluteUrl(link)); + a.setAttribute('title', short((this.isCustomLink(link)) ? + this.getLinkTitle(link) : link, 80)); + + if (this.linkTarget != null) + { + a.setAttribute('target', this.linkTarget); + } + + // Adds shortened label to link + mxUtils.write(a, short(label, 40)); + + // Handles custom links + if (this.isCustomLink(link)) + { + mxEvent.addListener(a, 'click', mxUtils.bind(this, function(evt) + { + this.customLinkClicked(link); + mxEvent.consume(evt); + })); + } + + return a; + }; + + /** + * Customized graph for touch devices. + */ + Graph.prototype.initTouch = function() + { + // Disables new connections via "hotspot" + this.connectionHandler.marker.isEnabled = function() + { + return this.graph.connectionHandler.first != null; + }; + + // Hides menu when editing starts + this.addListener(mxEvent.START_EDITING, function(sender, evt) + { + this.popupMenuHandler.hideMenu(); + }); + + // Adds custom hit detection if native hit detection found no cell + var graphUpdateMouseEvent = this.updateMouseEvent; + this.updateMouseEvent = function(me) + { + me = graphUpdateMouseEvent.apply(this, arguments); + + if (mxEvent.isTouchEvent(me.getEvent()) && me.getState() == null) + { + var cell = this.getCellAt(me.graphX, me.graphY); + + if (cell != null && this.isSwimlane(cell) && this.hitsSwimlaneContent(cell, me.graphX, me.graphY)) + { + cell = null; + } + else + { + me.state = this.view.getState(cell); + + if (me.state != null && me.state.shape != null) + { + this.container.style.cursor = me.state.shape.node.style.cursor; + } + } + } + + if (me.getState() == null && this.isEnabled()) + { + this.container.style.cursor = 'default'; + } + + return me; + }; + + // Context menu trigger implementation depending on current selection state + // combined with support for normal popup trigger. + var cellSelected = false; + var selectionEmpty = false; + var menuShowing = false; + + var oldFireMouseEvent = this.fireMouseEvent; + + this.fireMouseEvent = function(evtName, me, sender) + { + if (evtName == mxEvent.MOUSE_DOWN) + { + // For hit detection on edges + me = this.updateMouseEvent(me); + + cellSelected = this.isCellSelected(me.getCell()); + selectionEmpty = this.isSelectionEmpty(); + menuShowing = this.popupMenuHandler.isMenuShowing(); + } + + oldFireMouseEvent.apply(this, arguments); + }; + + // Shows popup menu if cell was selected or selection was empty and background was clicked + // FIXME: Conflicts with mxPopupMenuHandler.prototype.getCellForPopupEvent in Editor.js by + // selecting parent for selected children in groups before this check can be made. + this.popupMenuHandler.mouseUp = mxUtils.bind(this, function(sender, me) + { + this.popupMenuHandler.popupTrigger = !this.isEditing() && this.isEnabled() && + (me.getState() == null || !me.isSource(me.getState().control)) && + (this.popupMenuHandler.popupTrigger || (!menuShowing && !mxEvent.isMouseEvent(me.getEvent()) && + ((selectionEmpty && me.getCell() == null && this.isSelectionEmpty()) || + (cellSelected && this.isCellSelected(me.getCell()))))); + mxPopupMenuHandler.prototype.mouseUp.apply(this.popupMenuHandler, arguments); + }); + }; + + /** + * HTML in-place editor + */ + mxCellEditor.prototype.isContentEditing = function() + { + var state = this.graph.view.getState(this.editingCell); + + return state != null && state.style['html'] == 1; + }; + + /** + * Returns true if all selected text is inside a table element. + */ + mxCellEditor.prototype.isTableSelected = function() + { + return this.graph.getParentByName( + this.graph.getSelectedElement(), + 'TABLE', this.textarea) != null; + }; + + /** + * Sets the alignment of the current selected cell. This sets the + * alignment in the cell style, removes all alignment within the + * text and invokes the built-in alignment function. + * + * Only the built-in function is invoked if shift is pressed or + * if table cells are selected and shift is not pressed. + */ + mxCellEditor.prototype.alignText = function(align, evt) + { + var shiftPressed = evt != null && mxEvent.isShiftDown(evt); + + if (shiftPressed || (window.getSelection != null && window.getSelection().containsNode != null)) + { + var allSelected = true; + + this.graph.processElements(this.textarea, function(node) + { + if (shiftPressed || window.getSelection().containsNode(node, true)) + { + node.removeAttribute('align'); + node.style.textAlign = null; + } + else + { + allSelected = false; + } + }); + + if (allSelected) + { + this.graph.cellEditor.setAlign(align); + } + } + + document.execCommand('justify' + align.toLowerCase(), false, null); + }; + + /** + * Creates the keyboard event handler for the current graph and history. + */ + mxCellEditor.prototype.saveSelection = function() + { + if (window.getSelection) + { + var sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var ranges = []; + + for (var i = 0, len = sel.rangeCount; i < len; ++i) + { + ranges.push(sel.getRangeAt(i)); + } + + return ranges; + } + } + else if (document.selection && document.selection.createRange) + { + return document.selection.createRange(); + } + + return null; + }; + + /** + * Creates the keyboard event handler for the current graph and history. + */ + mxCellEditor.prototype.restoreSelection = function(savedSel) + { + try + { + if (savedSel) + { + if (window.getSelection) + { + sel = window.getSelection(); + sel.removeAllRanges(); + + for (var i = 0, len = savedSel.length; i < len; ++i) + { + sel.addRange(savedSel[i]); + } + } + else if (document.selection && savedSel.select) + { + savedSel.select(); + } + } + } + catch (e) + { + // ignore + } + }; + + /** + * Handling of special nl2Br style for not converting newlines to breaks in HTML labels. + * NOTE: Since it's easier to set this when the label is created we assume that it does + * not change during the lifetime of the mxText instance. + */ + var mxCellRendererInitializeLabel = mxCellRenderer.prototype.initializeLabel; + mxCellRenderer.prototype.initializeLabel = function(state) + { + if (state.text != null) + { + state.text.replaceLinefeeds = mxUtils.getValue(state.style, 'nl2Br', '1') != '0'; + } + + mxCellRendererInitializeLabel.apply(this, arguments); + }; + + var mxConstraintHandlerUpdate = mxConstraintHandler.prototype.update; + mxConstraintHandler.prototype.update = function(me, source) + { + if (this.isKeepFocusEvent(me) || !mxEvent.isAltDown(me.getEvent())) + { + mxConstraintHandlerUpdate.apply(this, arguments); + } + else + { + this.reset(); + } + }; + + /** + * No dashed shapes. + */ + mxGuide.prototype.createGuideShape = function(horizontal) + { + var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); + + return guide; + }; + + /** + * HTML in-place editor + */ + mxCellEditor.prototype.escapeCancelsEditing = false; + + var mxCellEditorStartEditing = mxCellEditor.prototype.startEditing; + mxCellEditor.prototype.startEditing = function(cell, trigger) + { + mxCellEditorStartEditing.apply(this, arguments); + + // Overrides class in case of HTML content to add + // dashed borders for divs and table cells + var state = this.graph.view.getState(cell); + + if (state != null && state.style['html'] == 1) + { + this.textarea.className = 'mxCellEditor geContentEditable'; + } + else + { + this.textarea.className = 'mxCellEditor mxPlainTextEditor'; + } + + // Toggles markup vs wysiwyg mode + this.codeViewMode = false; + + // Stores current selection range when switching between markup and code + this.switchSelectionState = null; + + // Selects editing cell + this.graph.setSelectionCell(cell); + + // Enables focus outline for edges and edge labels + var parent = this.graph.getModel().getParent(cell); + var geo = this.graph.getCellGeometry(cell); + + if ((this.graph.getModel().isEdge(parent) && geo != null && geo.relative) || + this.graph.getModel().isEdge(cell)) + { + // Quirks does not support outline at all so use border instead + if (mxClient.IS_QUIRKS) + { + this.textarea.style.border = 'gray dotted 1px'; + } + // IE>8 and FF on Windows uses outline default of none + else if (mxClient.IS_IE || mxClient.IS_IE11 || (mxClient.IS_FF && mxClient.IS_WIN)) + { + this.textarea.style.outline = 'gray dotted 1px'; + } + else + { + this.textarea.style.outline = ''; + } + } + else if (mxClient.IS_QUIRKS) + { + this.textarea.style.outline = 'none'; + this.textarea.style.border = ''; + } + } + + /** + * HTML in-place editor + */ + var cellEditorInstallListeners = mxCellEditor.prototype.installListeners; + mxCellEditor.prototype.installListeners = function(elt) + { + cellEditorInstallListeners.apply(this, arguments); + + // Adds a reference from the clone to the original node, recursively + function reference(node, clone) + { + clone.originalNode = node; + + node = node.firstChild; + var child = clone.firstChild; + + while (node != null && child != null) + { + reference(node, child); + node = node.nextSibling; + child = child.nextSibling; + } + + return clone; + }; + + // Checks the given node for new nodes, recursively + function checkNode(node, clone) + { + if (node != null) + { + if (clone.originalNode != node) + { + cleanNode(node); + } + else + { + node = node.firstChild; + clone = clone.firstChild; + + while (node != null) + { + var nextNode = node.nextSibling; + + if (clone == null) + { + cleanNode(node); + } + else + { + checkNode(node, clone); + clone = clone.nextSibling; + } + + node = nextNode; + } + } + } + }; + + // Removes unused DOM nodes and attributes, recursively + function cleanNode(node) + { + var child = node.firstChild; + + while (child != null) + { + var next = child.nextSibling; + cleanNode(child); + child = next; + } + + if ((node.nodeType != 1 || (node.nodeName !== 'BR' && node.firstChild == null)) && + (node.nodeType != 3 || mxUtils.trim(mxUtils.getTextContent(node)).length == 0)) + { + node.parentNode.removeChild(node); + } + else + { + // Removes linefeeds + if (node.nodeType == 3) + { + mxUtils.setTextContent(node, mxUtils.getTextContent(node).replace(/\n|\r/g, '')); + } + + // Removes CSS classes and styles (for Word and Excel) + if (node.nodeType == 1) + { + node.removeAttribute('style'); + node.removeAttribute('class'); + node.removeAttribute('width'); + node.removeAttribute('cellpadding'); + node.removeAttribute('cellspacing'); + node.removeAttribute('border'); + } + } + }; + + // Handles paste from Word, Excel etc by removing styles, classnames and unused nodes + // LATER: Fix undo/redo for paste + if (!mxClient.IS_QUIRKS && document.documentMode !== 7 && document.documentMode !== 8) + { + mxEvent.addListener(this.textarea, 'paste', mxUtils.bind(this, function(evt) + { + var clone = reference(this.textarea, this.textarea.cloneNode(true)); + + window.setTimeout(mxUtils.bind(this, function() + { + // Paste from Word or Excel + if (this.textarea != null && + (this.textarea.innerHTML.indexOf('') >= 0 || + this.textarea.innerHTML.indexOf(' + + + + Grapheditor viewer + + + + + + + + + + Input: +
+ +
+ +
+ + + diff --git a/static/mxgraph/examples/graphlayout.html b/static/mxgraph/examples/graphlayout.html new file mode 100644 index 0000000..85d5873 --- /dev/null +++ b/static/mxgraph/examples/graphlayout.html @@ -0,0 +1,196 @@ + + + + Graphlayout example for mxGraph + + + + + + + + + + + + + + + +
+
+
+ Transitions + + diff --git a/static/mxgraph/examples/grid.html b/static/mxgraph/examples/grid.html new file mode 100644 index 0000000..fe94d49 --- /dev/null +++ b/static/mxgraph/examples/grid.html @@ -0,0 +1,216 @@ + + + + + + Grid example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/groups.html b/static/mxgraph/examples/groups.html new file mode 100644 index 0000000..1100027 --- /dev/null +++ b/static/mxgraph/examples/groups.html @@ -0,0 +1,178 @@ + + + + Hello, World! example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/guides.html b/static/mxgraph/examples/guides.html new file mode 100644 index 0000000..af3863a --- /dev/null +++ b/static/mxgraph/examples/guides.html @@ -0,0 +1,163 @@ + + + + Guides example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/handles.html b/static/mxgraph/examples/handles.html new file mode 100644 index 0000000..68be673 --- /dev/null +++ b/static/mxgraph/examples/handles.html @@ -0,0 +1,192 @@ + + + + Handles example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/helloport.html b/static/mxgraph/examples/helloport.html new file mode 100644 index 0000000..55a5bff --- /dev/null +++ b/static/mxgraph/examples/helloport.html @@ -0,0 +1,122 @@ + + + + Hello, World! example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/helloworld.html b/static/mxgraph/examples/helloworld.html new file mode 100644 index 0000000..0a95d9c --- /dev/null +++ b/static/mxgraph/examples/helloworld.html @@ -0,0 +1,73 @@ + + + + Hello, World! example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/hierarchicallayout.html b/static/mxgraph/examples/hierarchicallayout.html new file mode 100644 index 0000000..92d5c9f --- /dev/null +++ b/static/mxgraph/examples/hierarchicallayout.html @@ -0,0 +1,138 @@ + + + + Hierarchical Layout example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/hovericons.html b/static/mxgraph/examples/hovericons.html new file mode 100644 index 0000000..8160bd4 --- /dev/null +++ b/static/mxgraph/examples/hovericons.html @@ -0,0 +1,222 @@ + + + + Hover icons example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/hoverstyle.html b/static/mxgraph/examples/hoverstyle.html new file mode 100644 index 0000000..35daf65 --- /dev/null +++ b/static/mxgraph/examples/hoverstyle.html @@ -0,0 +1,165 @@ + + + + Hoverstyle example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/htmllabel.html b/static/mxgraph/examples/htmllabel.html new file mode 100644 index 0000000..c1ab7d1 --- /dev/null +++ b/static/mxgraph/examples/htmllabel.html @@ -0,0 +1,174 @@ + + + + HTML label example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/ie9svg.html b/static/mxgraph/examples/ie9svg.html new file mode 100644 index 0000000..0fdd59b --- /dev/null +++ b/static/mxgraph/examples/ie9svg.html @@ -0,0 +1,78 @@ + + + + IE9 SVG example for mxGraph + + + + + + + + + + + + + + + + + + +
+
+
+ See also:
+ Standardsmode
+ docs/known-issues.html#Doctypes
+ docs/known-issues.html#IE9 + + diff --git a/static/mxgraph/examples/images.html b/static/mxgraph/examples/images.html new file mode 100644 index 0000000..5be29ed --- /dev/null +++ b/static/mxgraph/examples/images.html @@ -0,0 +1,128 @@ + + + + Images example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/images/add.png b/static/mxgraph/examples/images/add.png new file mode 100644 index 0000000..bf5f8ed Binary files /dev/null and b/static/mxgraph/examples/images/add.png differ diff --git a/static/mxgraph/examples/images/camera.png b/static/mxgraph/examples/images/camera.png new file mode 100644 index 0000000..aecc94d Binary files /dev/null and b/static/mxgraph/examples/images/camera.png differ diff --git a/static/mxgraph/examples/images/check.png b/static/mxgraph/examples/images/check.png new file mode 100644 index 0000000..ce81bce Binary files /dev/null and b/static/mxgraph/examples/images/check.png differ diff --git a/static/mxgraph/examples/images/close.png b/static/mxgraph/examples/images/close.png new file mode 100644 index 0000000..4de4396 Binary files /dev/null and b/static/mxgraph/examples/images/close.png differ diff --git a/static/mxgraph/examples/images/connector.gif b/static/mxgraph/examples/images/connector.gif new file mode 100644 index 0000000..326e061 Binary files /dev/null and b/static/mxgraph/examples/images/connector.gif differ diff --git a/static/mxgraph/examples/images/copy.png b/static/mxgraph/examples/images/copy.png new file mode 100644 index 0000000..a987d43 Binary files /dev/null and b/static/mxgraph/examples/images/copy.png differ diff --git a/static/mxgraph/examples/images/cut.png b/static/mxgraph/examples/images/cut.png new file mode 100644 index 0000000..52bf944 Binary files /dev/null and b/static/mxgraph/examples/images/cut.png differ diff --git a/static/mxgraph/examples/images/delete2.png b/static/mxgraph/examples/images/delete2.png new file mode 100644 index 0000000..be78c61 Binary files /dev/null and b/static/mxgraph/examples/images/delete2.png differ diff --git a/static/mxgraph/examples/images/dot.gif b/static/mxgraph/examples/images/dot.gif new file mode 100644 index 0000000..08b9947 Binary files /dev/null and b/static/mxgraph/examples/images/dot.gif differ diff --git a/static/mxgraph/examples/images/export1.png b/static/mxgraph/examples/images/export1.png new file mode 100644 index 0000000..b8a01b8 Binary files /dev/null and b/static/mxgraph/examples/images/export1.png differ diff --git a/static/mxgraph/examples/images/fit_to_size.png b/static/mxgraph/examples/images/fit_to_size.png new file mode 100644 index 0000000..4de46b0 Binary files /dev/null and b/static/mxgraph/examples/images/fit_to_size.png differ diff --git a/static/mxgraph/examples/images/gradient_background.jpg b/static/mxgraph/examples/images/gradient_background.jpg new file mode 100644 index 0000000..7dbf35b Binary files /dev/null and b/static/mxgraph/examples/images/gradient_background.jpg differ diff --git a/static/mxgraph/examples/images/green-dot.gif b/static/mxgraph/examples/images/green-dot.gif new file mode 100644 index 0000000..acaf7b2 Binary files /dev/null and b/static/mxgraph/examples/images/green-dot.gif differ diff --git a/static/mxgraph/examples/images/group.png b/static/mxgraph/examples/images/group.png new file mode 100644 index 0000000..585ad79 Binary files /dev/null and b/static/mxgraph/examples/images/group.png differ diff --git a/static/mxgraph/examples/images/handle-connect.png b/static/mxgraph/examples/images/handle-connect.png new file mode 100644 index 0000000..513ab2b Binary files /dev/null and b/static/mxgraph/examples/images/handle-connect.png differ diff --git a/static/mxgraph/examples/images/handle-main.png b/static/mxgraph/examples/images/handle-main.png new file mode 100644 index 0000000..ee067ff Binary files /dev/null and b/static/mxgraph/examples/images/handle-main.png differ diff --git a/static/mxgraph/examples/images/icons48/column.png b/static/mxgraph/examples/images/icons48/column.png new file mode 100644 index 0000000..5ae2c24 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/column.png differ diff --git a/static/mxgraph/examples/images/icons48/earth.png b/static/mxgraph/examples/images/icons48/earth.png new file mode 100644 index 0000000..4493880 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/earth.png differ diff --git a/static/mxgraph/examples/images/icons48/gear.png b/static/mxgraph/examples/images/icons48/gear.png new file mode 100644 index 0000000..647d897 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/gear.png differ diff --git a/static/mxgraph/examples/images/icons48/keys.png b/static/mxgraph/examples/images/icons48/keys.png new file mode 100644 index 0000000..41828e4 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/keys.png differ diff --git a/static/mxgraph/examples/images/icons48/mail_new.png b/static/mxgraph/examples/images/icons48/mail_new.png new file mode 100644 index 0000000..16c6662 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/mail_new.png differ diff --git a/static/mxgraph/examples/images/icons48/server.png b/static/mxgraph/examples/images/icons48/server.png new file mode 100644 index 0000000..9621c6e Binary files /dev/null and b/static/mxgraph/examples/images/icons48/server.png differ diff --git a/static/mxgraph/examples/images/icons48/table.png b/static/mxgraph/examples/images/icons48/table.png new file mode 100644 index 0000000..d4df646 Binary files /dev/null and b/static/mxgraph/examples/images/icons48/table.png differ diff --git a/static/mxgraph/examples/images/key.png b/static/mxgraph/examples/images/key.png new file mode 100644 index 0000000..e66758a Binary files /dev/null and b/static/mxgraph/examples/images/key.png differ diff --git a/static/mxgraph/examples/images/loading.gif b/static/mxgraph/examples/images/loading.gif new file mode 100644 index 0000000..118f4b0 Binary files /dev/null and b/static/mxgraph/examples/images/loading.gif differ diff --git a/static/mxgraph/examples/images/navigate_minus.png b/static/mxgraph/examples/images/navigate_minus.png new file mode 100644 index 0000000..71edaf9 Binary files /dev/null and b/static/mxgraph/examples/images/navigate_minus.png differ diff --git a/static/mxgraph/examples/images/navigate_plus.png b/static/mxgraph/examples/images/navigate_plus.png new file mode 100644 index 0000000..b5b7e87 Binary files /dev/null and b/static/mxgraph/examples/images/navigate_plus.png differ diff --git a/static/mxgraph/examples/images/paste.png b/static/mxgraph/examples/images/paste.png new file mode 100644 index 0000000..fd628d9 Binary files /dev/null and b/static/mxgraph/examples/images/paste.png differ diff --git a/static/mxgraph/examples/images/plus.png b/static/mxgraph/examples/images/plus.png new file mode 100644 index 0000000..24a84bb Binary files /dev/null and b/static/mxgraph/examples/images/plus.png differ diff --git a/static/mxgraph/examples/images/press32.png b/static/mxgraph/examples/images/press32.png new file mode 100644 index 0000000..f00e3f7 Binary files /dev/null and b/static/mxgraph/examples/images/press32.png differ diff --git a/static/mxgraph/examples/images/print32.png b/static/mxgraph/examples/images/print32.png new file mode 100644 index 0000000..0cca86c Binary files /dev/null and b/static/mxgraph/examples/images/print32.png differ diff --git a/static/mxgraph/examples/images/printer.png b/static/mxgraph/examples/images/printer.png new file mode 100644 index 0000000..6004816 Binary files /dev/null and b/static/mxgraph/examples/images/printer.png differ diff --git a/static/mxgraph/examples/images/redo.png b/static/mxgraph/examples/images/redo.png new file mode 100644 index 0000000..3eae59c Binary files /dev/null and b/static/mxgraph/examples/images/redo.png differ diff --git a/static/mxgraph/examples/images/sidebar_bg.gif b/static/mxgraph/examples/images/sidebar_bg.gif new file mode 100644 index 0000000..67e8244 Binary files /dev/null and b/static/mxgraph/examples/images/sidebar_bg.gif differ diff --git a/static/mxgraph/examples/images/spacer.gif b/static/mxgraph/examples/images/spacer.gif new file mode 100644 index 0000000..35d42e8 Binary files /dev/null and b/static/mxgraph/examples/images/spacer.gif differ diff --git a/static/mxgraph/examples/images/toolbar_bg.gif b/static/mxgraph/examples/images/toolbar_bg.gif new file mode 100644 index 0000000..87b9374 Binary files /dev/null and b/static/mxgraph/examples/images/toolbar_bg.gif differ diff --git a/static/mxgraph/examples/images/undo.png b/static/mxgraph/examples/images/undo.png new file mode 100644 index 0000000..4ba0ffb Binary files /dev/null and b/static/mxgraph/examples/images/undo.png differ diff --git a/static/mxgraph/examples/images/view_1_1.png b/static/mxgraph/examples/images/view_1_1.png new file mode 100644 index 0000000..88657a1 Binary files /dev/null and b/static/mxgraph/examples/images/view_1_1.png differ diff --git a/static/mxgraph/examples/images/view_1_132.png b/static/mxgraph/examples/images/view_1_132.png new file mode 100644 index 0000000..e9a1b72 Binary files /dev/null and b/static/mxgraph/examples/images/view_1_132.png differ diff --git a/static/mxgraph/examples/images/view_next.png b/static/mxgraph/examples/images/view_next.png new file mode 100644 index 0000000..b4094f0 Binary files /dev/null and b/static/mxgraph/examples/images/view_next.png differ diff --git a/static/mxgraph/examples/images/view_previous.png b/static/mxgraph/examples/images/view_previous.png new file mode 100644 index 0000000..b385b44 Binary files /dev/null and b/static/mxgraph/examples/images/view_previous.png differ diff --git a/static/mxgraph/examples/images/wires-grid.gif b/static/mxgraph/examples/images/wires-grid.gif new file mode 100644 index 0000000..ad888a2 Binary files /dev/null and b/static/mxgraph/examples/images/wires-grid.gif differ diff --git a/static/mxgraph/examples/images/zoom_in.png b/static/mxgraph/examples/images/zoom_in.png new file mode 100644 index 0000000..ad6abb9 Binary files /dev/null and b/static/mxgraph/examples/images/zoom_in.png differ diff --git a/static/mxgraph/examples/images/zoom_in32.png b/static/mxgraph/examples/images/zoom_in32.png new file mode 100644 index 0000000..438ff0f Binary files /dev/null and b/static/mxgraph/examples/images/zoom_in32.png differ diff --git a/static/mxgraph/examples/images/zoom_out.png b/static/mxgraph/examples/images/zoom_out.png new file mode 100644 index 0000000..0566f26 Binary files /dev/null and b/static/mxgraph/examples/images/zoom_out.png differ diff --git a/static/mxgraph/examples/images/zoom_out32.png b/static/mxgraph/examples/images/zoom_out32.png new file mode 100644 index 0000000..8edb765 Binary files /dev/null and b/static/mxgraph/examples/images/zoom_out32.png differ diff --git a/static/mxgraph/examples/indicators.html b/static/mxgraph/examples/indicators.html new file mode 100644 index 0000000..0fc6b10 --- /dev/null +++ b/static/mxgraph/examples/indicators.html @@ -0,0 +1,97 @@ + + + + Indicators example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/jquery.html b/static/mxgraph/examples/jquery.html new file mode 100644 index 0000000..85b751d --- /dev/null +++ b/static/mxgraph/examples/jquery.html @@ -0,0 +1,199 @@ + + + + JQuery plugin for labels + + + + + + + + + + + + + + + + + + +
+
+ + + diff --git a/static/mxgraph/examples/jsondata.html b/static/mxgraph/examples/jsondata.html new file mode 100644 index 0000000..a8afbbe --- /dev/null +++ b/static/mxgraph/examples/jsondata.html @@ -0,0 +1,108 @@ + + + + JSON data example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/labelposition.html b/static/mxgraph/examples/labelposition.html new file mode 100644 index 0000000..a602a93 --- /dev/null +++ b/static/mxgraph/examples/labelposition.html @@ -0,0 +1,77 @@ + + + + Label Position example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/labels.html b/static/mxgraph/examples/labels.html new file mode 100644 index 0000000..6fa210a --- /dev/null +++ b/static/mxgraph/examples/labels.html @@ -0,0 +1,142 @@ + + + + Hello, World! example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/layers.html b/static/mxgraph/examples/layers.html new file mode 100644 index 0000000..f488343 --- /dev/null +++ b/static/mxgraph/examples/layers.html @@ -0,0 +1,98 @@ + + + + Layers example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/lod.html b/static/mxgraph/examples/lod.html new file mode 100644 index 0000000..c0ee48b --- /dev/null +++ b/static/mxgraph/examples/lod.html @@ -0,0 +1,95 @@ + + + + Level of detail example for mxGraph + + + + + + + + + + + + + + + +
+
+ + diff --git a/static/mxgraph/examples/map-background/images/layers-2x.png b/static/mxgraph/examples/map-background/images/layers-2x.png new file mode 100644 index 0000000..200c333 Binary files /dev/null and b/static/mxgraph/examples/map-background/images/layers-2x.png differ diff --git a/static/mxgraph/examples/map-background/images/layers.png b/static/mxgraph/examples/map-background/images/layers.png new file mode 100644 index 0000000..1a72e57 Binary files /dev/null and b/static/mxgraph/examples/map-background/images/layers.png differ diff --git a/static/mxgraph/examples/map-background/images/marker-icon-2x.png b/static/mxgraph/examples/map-background/images/marker-icon-2x.png new file mode 100644 index 0000000..88f9e50 Binary files /dev/null and b/static/mxgraph/examples/map-background/images/marker-icon-2x.png differ diff --git a/static/mxgraph/examples/map-background/images/marker-icon.png b/static/mxgraph/examples/map-background/images/marker-icon.png new file mode 100644 index 0000000..950edf2 Binary files /dev/null and b/static/mxgraph/examples/map-background/images/marker-icon.png differ diff --git a/static/mxgraph/examples/map-background/images/marker-shadow.png b/static/mxgraph/examples/map-background/images/marker-shadow.png new file mode 100644 index 0000000..9fd2979 Binary files /dev/null and b/static/mxgraph/examples/map-background/images/marker-shadow.png differ diff --git a/static/mxgraph/examples/map-background/leaflet-src.js b/static/mxgraph/examples/map-background/leaflet-src.js new file mode 100644 index 0000000..1dd3923 --- /dev/null +++ b/static/mxgraph/examples/map-background/leaflet-src.js @@ -0,0 +1,13802 @@ +/* @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 (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.L = {}))); +}(this, (function (exports) { 'use strict'; + +var version = "1.3.1+HEAD.ba6f97f"; + +/* + * @namespace Util + * + * Various utility functions, used by Leaflet internally. + */ + +var freeze = Object.freeze; +Object.freeze = function (obj) { return obj; }; + +// @function extend(dest: Object, src?: Object): Object +// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. +function extend(dest) { + var i, j, len, src; + + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; + for (i in src) { + dest[i] = src[i]; + } + } + return dest; +} + +// @function create(proto: Object, properties?: Object): Object +// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) +var create = Object.create || (function () { + function F() {} + return function (proto) { + F.prototype = proto; + return new F(); + }; +})(); + +// @function bind(fn: Function, …): Function +// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). +// Has a `L.bind()` shortcut. +function bind(fn, obj) { + var slice = Array.prototype.slice; + + if (fn.bind) { + return fn.bind.apply(fn, slice.call(arguments, 1)); + } + + var args = slice.call(arguments, 2); + + return function () { + return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); + }; +} + +// @property lastId: Number +// Last unique ID used by [`stamp()`](#util-stamp) +var lastId = 0; + +// @function stamp(obj: Object): Number +// Returns the unique ID of an object, assigning it one if it doesn't have it. +function stamp(obj) { + /*eslint-disable */ + obj._leaflet_id = obj._leaflet_id || ++lastId; + return obj._leaflet_id; + /* eslint-enable */ +} + +// @function throttle(fn: Function, time: Number, context: Object): Function +// Returns a function which executes function `fn` with the given scope `context` +// (so that the `this` keyword refers to `context` inside `fn`'s code). The function +// `fn` will be called no more than one time per given amount of `time`. The arguments +// received by the bound function will be any arguments passed when binding the +// function, followed by any arguments passed when invoking the bound function. +// Has an `L.throttle` shortcut. +function throttle(fn, time, context) { + var lock, args, wrapperFn, later; + + later = function () { + // reset lock and call if queued + lock = false; + if (args) { + wrapperFn.apply(context, args); + args = false; + } + }; + + wrapperFn = function () { + if (lock) { + // called too soon, queue to call later + args = arguments; + + } else { + // call and lock until later + fn.apply(context, arguments); + setTimeout(later, time); + lock = true; + } + }; + + return wrapperFn; +} + +// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number +// Returns the number `num` modulo `range` in such a way so it lies within +// `range[0]` and `range[1]`. The returned value will be always smaller than +// `range[1]` unless `includeMax` is set to `true`. +function wrapNum(x, range, includeMax) { + var max = range[1], + min = range[0], + d = max - min; + return x === max && includeMax ? x : ((x - min) % d + d) % d + min; +} + +// @function falseFn(): Function +// Returns a function which always returns `false`. +function falseFn() { return false; } + +// @function formatNum(num: Number, digits?: Number): Number +// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. +function formatNum(num, digits) { + var pow = Math.pow(10, (digits === undefined ? 6 : digits)); + return Math.round(num * pow) / pow; +} + +// @function trim(str: String): String +// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) +function trim(str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); +} + +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. +function splitWords(str) { + return trim(str).split(/\s+/); +} + +// @function setOptions(obj: Object, options: Object): Object +// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. +function setOptions(obj, options) { + if (!obj.hasOwnProperty('options')) { + obj.options = obj.options ? create(obj.options) : {}; + } + for (var i in options) { + obj.options[i] = options[i]; + } + return obj.options; +} + +// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String +// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` +// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will +// be appended at the end. If `uppercase` is `true`, the parameter names will +// be uppercased (e.g. `'?A=foo&B=bar'`) +function getParamString(obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); +} + +var templateRe = /\{ *([\w_-]+) *\}/g; + +// @function template(str: String, data: Object): String +// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` +// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string +// `('Hello foo, bar')`. You can also specify functions instead of strings for +// data values — they will be evaluated passing `data` as an argument. +function template(str, data) { + return str.replace(templateRe, function (str, key) { + var value = data[key]; + + if (value === undefined) { + throw new Error('No value provided for variable ' + str); + + } else if (typeof value === 'function') { + value = value(data); + } + return value; + }); +} + +// @function isArray(obj): Boolean +// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) +var isArray = Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); +}; + +// @function indexOf(array: Array, el: Object): Number +// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) +function indexOf(array, el) { + for (var i = 0; i < array.length; i++) { + if (array[i] === el) { return i; } + } + return -1; +} + +// @property emptyImageUrl: String +// Data URI string containing a base64-encoded empty GIF image. +// Used as a hack to free memory from unused images on WebKit-powered +// mobile devices (by setting image `src` to this string). +var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; + +// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + +function getPrefixed(name) { + return window['webkit' + name] || window['moz' + name] || window['ms' + name]; +} + +var lastTime = 0; + +// fallback for IE 7-8 +function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); + + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); +} + +var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; +var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; + +// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number +// Schedules `fn` to be executed when the browser repaints. `fn` is bound to +// `context` if given. When `immediate` is set, `fn` is called immediately if +// the browser doesn't have native support for +// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), +// otherwise it's delayed. Returns a request ID that can be used to cancel the request. +function requestAnimFrame(fn, context, immediate) { + if (immediate && requestFn === timeoutDefer) { + fn.call(context); + } else { + return requestFn.call(window, bind(fn, context)); + } +} + +// @function cancelAnimFrame(id: Number): undefined +// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). +function cancelAnimFrame(id) { + if (id) { + cancelFn.call(window, id); + } +} + + +var Util = (Object.freeze || Object)({ + freeze: freeze, + extend: extend, + create: create, + bind: bind, + lastId: lastId, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame +}); + +// @class Class +// @aka L.Class + +// @section +// @uninheritable + +// Thanks to John Resig and Dean Edwards for inspiration! + +function Class() {} + +Class.extend = function (props) { + + // @function extend(props: Object): Function + // [Extends the current class](#class-inheritance) given the properties to be included. + // Returns a Javascript function that is a class constructor (to be called with `new`). + var NewClass = function () { + + // call the constructor + if (this.initialize) { + this.initialize.apply(this, arguments); + } + + // call all constructor hooks + this.callInitHooks(); + }; + + var parentProto = NewClass.__super__ = this.prototype; + + var proto = create(parentProto); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + // inherit parent's statics + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + extend(NewClass, props.statics); + delete props.statics; + } + + // mix includes into the prototype + if (props.includes) { + checkDeprecatedMixinEvents(props.includes); + extend.apply(null, [proto].concat(props.includes)); + delete props.includes; + } + + // merge options + if (proto.options) { + props.options = extend(create(proto.options), props.options); + } + + // mix given properties into the prototype + extend(proto, props); + + proto._initHooks = []; + + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parentProto.callInitHooks) { + parentProto.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + + return NewClass; +}; + + +// @function include(properties: Object): this +// [Includes a mixin](#class-includes) into the current class. +Class.include = function (props) { + extend(this.prototype, props); + return this; +}; + +// @function mergeOptions(options: Object): this +// [Merges `options`](#class-options) into the defaults of the class. +Class.mergeOptions = function (options) { + extend(this.prototype.options, options); + return this; +}; + +// @function addInitHook(fn: Function): this +// Adds a [constructor hook](#class-constructor-hooks) to the class. +Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); + return this; +}; + +function checkDeprecatedMixinEvents(includes) { + if (typeof L === 'undefined' || !L || !L.Mixin) { return; } + + includes = isArray(includes) ? includes : [includes]; + + for (var i = 0; i < includes.length; i++) { + if (includes[i] === L.Mixin.Events) { + console.warn('Deprecated include of L.Mixin.Events: ' + + 'this property will be removed in future releases, ' + + 'please inherit from L.Evented instead.', new Error().stack); + } + } +} + +/* + * @class Evented + * @aka L.Evented + * @inherits Class + * + * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). + * + * @example + * + * ```js + * map.on('click', function(e) { + * alert(e.latlng); + * } ); + * ``` + * + * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: + * + * ```js + * function onClick(e) { ... } + * + * map.on('click', onClick); + * map.off('click', onClick); + * ``` + */ + +var Events = { + /* @method on(type: String, fn: Function, context?: Object): this + * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). + * + * @alternative + * @method on(eventMap: Object): this + * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + */ + on: function (types, fn, context) { + + // types can be a map of types/handlers + if (typeof types === 'object') { + for (var type in types) { + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn); + } + + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context); + } + } + + return this; + }, + + /* @method off(type: String, fn?: Function, context?: Object): this + * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. + * + * @alternative + * @method off(eventMap: Object): this + * Removes a set of type/listener pairs. + * + * @alternative + * @method off: this + * Removes all listeners to all events on the object. + */ + off: function (types, fn, context) { + + if (!types) { + // clear all listeners if called without arguments + delete this._events; + + } else if (typeof types === 'object') { + for (var type in types) { + this._off(type, types[type], fn); + } + + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._off(types[i], fn, context); + } + } + + return this; + }, + + // attach listener (without syntactic sugar now) + _on: function (type, fn, context) { + this._events = this._events || {}; + + /* get/init listeners for type */ + var typeListeners = this._events[type]; + if (!typeListeners) { + typeListeners = []; + this._events[type] = typeListeners; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + var newListener = {fn: fn, ctx: context}, + listeners = typeListeners; + + // check if fn already there + for (var i = 0, len = listeners.length; i < len; i++) { + if (listeners[i].fn === fn && listeners[i].ctx === context) { + return; + } + } + + listeners.push(newListener); + }, + + _off: function (type, fn, context) { + var listeners, + i, + len; + + if (!this._events) { return; } + + listeners = this._events[type]; + + if (!listeners) { + return; + } + + if (!fn) { + // Set all removed listeners to noop so they are not called if remove happens in fire + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].fn = falseFn; + } + // clear all listeners for a type if function isn't specified + delete this._events[type]; + return; + } + + if (context === this) { + context = undefined; + } + + if (listeners) { + + // find fn and remove it + for (i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + if (l.ctx !== context) { continue; } + if (l.fn === fn) { + + // set the removed listener to noop so that's not called if remove happens in fire + l.fn = falseFn; + + if (this._firingCount) { + /* copy array in case events are being fired */ + this._events[type] = listeners = listeners.slice(); + } + listeners.splice(i, 1); + + return; + } + } + } + }, + + // @method fire(type: String, data?: Object, propagate?: Boolean): this + // Fires an event of the specified type. You can optionally provide an data + // object — the first argument of the listener function will contain its + // properties. The event can optionally be propagated to event parents. + fire: function (type, data, propagate) { + if (!this.listens(type, propagate)) { return this; } + + var event = extend({}, data, { + type: type, + target: this, + sourceTarget: data && data.sourceTarget || this + }); + + if (this._events) { + var listeners = this._events[type]; + + if (listeners) { + this._firingCount = (this._firingCount + 1) || 1; + for (var i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + l.fn.call(l.ctx || this, event); + } + + this._firingCount--; + } + } + + if (propagate) { + // propagate the event to parents (set with addEventParent) + this._propagateEvent(event); + } + + return this; + }, + + // @method listens(type: String): Boolean + // Returns `true` if a particular event type has any listeners attached to it. + listens: function (type, propagate) { + var listeners = this._events && this._events[type]; + if (listeners && listeners.length) { return true; } + + if (propagate) { + // also check parents for listeners if event propagates + for (var id in this._eventParents) { + if (this._eventParents[id].listens(type, propagate)) { return true; } + } + } + return false; + }, + + // @method once(…): this + // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. + once: function (types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + this.once(type, types[type], fn); + } + return this; + } + + var handler = bind(function () { + this + .off(types, fn, context) + .off(types, handler, context); + }, this); + + // add a listener that's executed once and removed after that + return this + .on(types, fn, context) + .on(types, handler, context); + }, + + // @method addEventParent(obj: Evented): this + // Adds an event parent - an `Evented` that will receive propagated events + addEventParent: function (obj) { + this._eventParents = this._eventParents || {}; + this._eventParents[stamp(obj)] = obj; + return this; + }, + + // @method removeEventParent(obj: Evented): this + // Removes an event parent, so it will stop receiving propagated events + removeEventParent: function (obj) { + if (this._eventParents) { + delete this._eventParents[stamp(obj)]; + } + return this; + }, + + _propagateEvent: function (e) { + for (var id in this._eventParents) { + this._eventParents[id].fire(e.type, extend({ + layer: e.target, + propagatedFrom: e.target + }, e), true); + } + } +}; + +// aliases; we should ditch those eventually + +// @method addEventListener(…): this +// Alias to [`on(…)`](#evented-on) +Events.addEventListener = Events.on; + +// @method removeEventListener(…): this +// Alias to [`off(…)`](#evented-off) + +// @method clearAllEventListeners(…): this +// Alias to [`off()`](#evented-off) +Events.removeEventListener = Events.clearAllEventListeners = Events.off; + +// @method addOneTimeEventListener(…): this +// Alias to [`once(…)`](#evented-once) +Events.addOneTimeEventListener = Events.once; + +// @method fireEvent(…): this +// Alias to [`fire(…)`](#evented-fire) +Events.fireEvent = Events.fire; + +// @method hasEventListeners(…): Boolean +// Alias to [`listens(…)`](#evented-listens) +Events.hasEventListeners = Events.listens; + +var Evented = Class.extend(Events); + +/* + * @class Point + * @aka L.Point + * + * Represents a point with `x` and `y` coordinates in pixels. + * + * @example + * + * ```js + * var point = L.point(200, 300); + * ``` + * + * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: + * + * ```js + * map.panBy([200, 300]); + * map.panBy(L.point(200, 300)); + * ``` + * + * Note that `Point` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Point(x, y, round) { + // @property x: Number; The `x` coordinate of the point + this.x = (round ? Math.round(x) : x); + // @property y: Number; The `y` coordinate of the point + this.y = (round ? Math.round(y) : y); +} + +var trunc = Math.trunc || function (v) { + return v > 0 ? Math.floor(v) : Math.ceil(v); +}; + +Point.prototype = { + + // @method clone(): Point + // Returns a copy of the current point. + clone: function () { + return new Point(this.x, this.y); + }, + + // @method add(otherPoint: Point): Point + // Returns the result of addition of the current and the given points. + add: function (point) { + // non-destructive, returns a new point + return this.clone()._add(toPoint(point)); + }, + + _add: function (point) { + // destructive, used directly for performance in situations where it's safe to modify existing point + this.x += point.x; + this.y += point.y; + return this; + }, + + // @method subtract(otherPoint: Point): Point + // Returns the result of subtraction of the given point from the current. + subtract: function (point) { + return this.clone()._subtract(toPoint(point)); + }, + + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + // @method divideBy(num: Number): Point + // Returns the result of division of the current point by the given number. + divideBy: function (num) { + return this.clone()._divideBy(num); + }, + + _divideBy: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + // @method multiplyBy(num: Number): Point + // Returns the result of multiplication of the current point by the given number. + multiplyBy: function (num) { + return this.clone()._multiplyBy(num); + }, + + _multiplyBy: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + // @method scaleBy(scale: Point): Point + // Multiply each coordinate of the current point by each coordinate of + // `scale`. In linear algebra terms, multiply the point by the + // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) + // defined by `scale`. + scaleBy: function (point) { + return new Point(this.x * point.x, this.y * point.y); + }, + + // @method unscaleBy(scale: Point): Point + // Inverse of `scaleBy`. Divide each coordinate of the current point by + // each coordinate of `scale`. + unscaleBy: function (point) { + return new Point(this.x / point.x, this.y / point.y); + }, + + // @method round(): Point + // Returns a copy of the current point with rounded coordinates. + round: function () { + return this.clone()._round(); + }, + + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + // @method floor(): Point + // Returns a copy of the current point with floored coordinates (rounded down). + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + // @method ceil(): Point + // Returns a copy of the current point with ceiled coordinates (rounded up). + ceil: function () { + return this.clone()._ceil(); + }, + + _ceil: function () { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + return this; + }, + + // @method trunc(): Point + // Returns a copy of the current point with truncated coordinates (rounded towards zero). + trunc: function () { + return this.clone()._trunc(); + }, + + _trunc: function () { + this.x = trunc(this.x); + this.y = trunc(this.y); + return this; + }, + + // @method distanceTo(otherPoint: Point): Number + // Returns the cartesian distance between the current and the given points. + distanceTo: function (point) { + point = toPoint(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + // @method equals(otherPoint: Point): Boolean + // Returns `true` if the given point has the same coordinates. + equals: function (point) { + point = toPoint(point); + + return point.x === this.x && + point.y === this.y; + }, + + // @method contains(otherPoint: Point): Boolean + // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). + contains: function (point) { + point = toPoint(point); + + return Math.abs(point.x) <= Math.abs(this.x) && + Math.abs(point.y) <= Math.abs(this.y); + }, + + // @method toString(): String + // Returns a string representation of the point for debugging purposes. + toString: function () { + return 'Point(' + + formatNum(this.x) + ', ' + + formatNum(this.y) + ')'; + } +}; + +// @factory L.point(x: Number, y: Number, round?: Boolean) +// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. + +// @alternative +// @factory L.point(coords: Number[]) +// Expects an array of the form `[x, y]` instead. + +// @alternative +// @factory L.point(coords: Object) +// Expects a plain object of the form `{x: Number, y: Number}` instead. +function toPoint(x, y, round) { + if (x instanceof Point) { + return x; + } + if (isArray(x)) { + return new Point(x[0], x[1]); + } + if (x === undefined || x === null) { + return x; + } + if (typeof x === 'object' && 'x' in x && 'y' in x) { + return new Point(x.x, x.y); + } + return new Point(x, y, round); +} + +/* + * @class Bounds + * @aka L.Bounds + * + * Represents a rectangular area in pixel coordinates. + * + * @example + * + * ```js + * var p1 = L.point(10, 10), + * p2 = L.point(40, 60), + * bounds = L.bounds(p1, p2); + * ``` + * + * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * otherBounds.intersects([[10, 10], [40, 60]]); + * ``` + * + * Note that `Bounds` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Bounds(a, b) { + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +} + +Bounds.prototype = { + // @method extend(point: Point): this + // Extends the bounds to contain the given point. + extend: function (point) { // (Point) + point = toPoint(point); + + // @property min: Point + // The top left corner of the rectangle. + // @property max: Point + // The bottom right corner of the rectangle. + if (!this.min && !this.max) { + this.min = point.clone(); + this.max = point.clone(); + } else { + this.min.x = Math.min(point.x, this.min.x); + this.max.x = Math.max(point.x, this.max.x); + this.min.y = Math.min(point.y, this.min.y); + this.max.y = Math.max(point.y, this.max.y); + } + return this; + }, + + // @method getCenter(round?: Boolean): Point + // Returns the center point of the bounds. + getCenter: function (round) { + return new Point( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + // @method getBottomLeft(): Point + // Returns the bottom-left point of the bounds. + getBottomLeft: function () { + return new Point(this.min.x, this.max.y); + }, + + // @method getTopRight(): Point + // Returns the top-right point of the bounds. + getTopRight: function () { // -> Point + return new Point(this.max.x, this.min.y); + }, + + // @method getTopLeft(): Point + // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). + getTopLeft: function () { + return this.min; // left, top + }, + + // @method getBottomRight(): Point + // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). + getBottomRight: function () { + return this.max; // right, bottom + }, + + // @method getSize(): Point + // Returns the size of the given bounds + getSize: function () { + return this.max.subtract(this.min); + }, + + // @method contains(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle contains the given one. + // @alternative + // @method contains(point: Point): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof Point) { + obj = toPoint(obj); + } else { + obj = toBounds(obj); + } + + if (obj instanceof Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + // @method intersects(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds + // intersect if they have at least one point in common. + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds + // overlap if their intersection is an area. + overlaps: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xOverlaps = (max2.x > min.x) && (min2.x < max.x), + yOverlaps = (max2.y > min.y) && (min2.y < max.y); + + return xOverlaps && yOverlaps; + }, + + isValid: function () { + return !!(this.min && this.max); + } +}; + + +// @factory L.bounds(corner1: Point, corner2: Point) +// Creates a Bounds object from two corners coordinate pairs. +// @alternative +// @factory L.bounds(points: Point[]) +// Creates a Bounds object from the given array of points. +function toBounds(a, b) { + if (!a || a instanceof Bounds) { + return a; + } + return new Bounds(a, b); +} + +/* + * @class LatLngBounds + * @aka L.LatLngBounds + * + * Represents a rectangular geographical area on a map. + * + * @example + * + * ```js + * var corner1 = L.latLng(40.712, -74.227), + * corner2 = L.latLng(40.774, -74.125), + * bounds = L.latLngBounds(corner1, corner2); + * ``` + * + * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * map.fitBounds([ + * [40.712, -74.227], + * [40.774, -74.125] + * ]); + * ``` + * + * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. + * + * Note that `LatLngBounds` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) + if (!corner1) { return; } + + var latlngs = corner2 ? [corner1, corner2] : corner1; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +} + +LatLngBounds.prototype = { + + // @method extend(latlng: LatLng): this + // Extend the bounds to contain the given point + + // @alternative + // @method extend(otherBounds: LatLngBounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLng) { + sw2 = obj; + ne2 = obj; + + } else if (obj instanceof LatLngBounds) { + sw2 = obj._southWest; + ne2 = obj._northEast; + + if (!sw2 || !ne2) { return this; } + + } else { + return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; + } + + if (!sw && !ne) { + this._southWest = new LatLng(sw2.lat, sw2.lng); + this._northEast = new LatLng(ne2.lat, ne2.lng); + } else { + sw.lat = Math.min(sw2.lat, sw.lat); + sw.lng = Math.min(sw2.lng, sw.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + ne.lng = Math.max(ne2.lng, ne.lng); + } + + return this; + }, + + // @method pad(bufferRatio: Number): LatLngBounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new LatLngBounds( + new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + // @method getCenter(): LatLng + // Returns the center point of the bounds. + getCenter: function () { + return new LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + // @method getSouthWest(): LatLng + // Returns the south-west point of the bounds. + getSouthWest: function () { + return this._southWest; + }, + + // @method getNorthEast(): LatLng + // Returns the north-east point of the bounds. + getNorthEast: function () { + return this._northEast; + }, + + // @method getNorthWest(): LatLng + // Returns the north-west point of the bounds. + getNorthWest: function () { + return new LatLng(this.getNorth(), this.getWest()); + }, + + // @method getSouthEast(): LatLng + // Returns the south-east point of the bounds. + getSouthEast: function () { + return new LatLng(this.getSouth(), this.getEast()); + }, + + // @method getWest(): Number + // Returns the west longitude of the bounds + getWest: function () { + return this._southWest.lng; + }, + + // @method getSouth(): Number + // Returns the south latitude of the bounds + getSouth: function () { + return this._southWest.lat; + }, + + // @method getEast(): Number + // Returns the east longitude of the bounds + getEast: function () { + return this._northEast.lng; + }, + + // @method getNorth(): Number + // Returns the north latitude of the bounds + getNorth: function () { + return this._northEast.lat; + }, + + // @method contains(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle contains the given one. + + // @alternative + // @method contains (latlng: LatLng): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { + obj = toLatLng(obj); + } else { + obj = toLatLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + // @method intersects(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. + intersects: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. + overlaps: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), + lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); + + return latOverlaps && lngOverlaps; + }, + + // @method toBBoxString(): String + // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, + + // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (bounds, maxMargin) { + if (!bounds) { return false; } + + bounds = toLatLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest(), maxMargin) && + this._northEast.equals(bounds.getNorthEast(), maxMargin); + }, + + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this._southWest && this._northEast); + } +}; + +// TODO International date line? + +// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) +// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. + +// @alternative +// @factory L.latLngBounds(latlngs: LatLng[]) +// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). +function toLatLngBounds(a, b) { + if (a instanceof LatLngBounds) { + return a; + } + return new LatLngBounds(a, b); +} + +/* @class LatLng + * @aka L.LatLng + * + * Represents a geographical point with a certain latitude and longitude. + * + * @example + * + * ``` + * var latlng = L.latLng(50.5, 30.5); + * ``` + * + * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: + * + * ``` + * map.panTo([50, 30]); + * map.panTo({lon: 30, lat: 50}); + * map.panTo({lat: 50, lng: 30}); + * map.panTo(L.latLng(50, 30)); + * ``` + * + * Note that `LatLng` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLng(lat, lng, alt) { + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); + } + + // @property lat: Number + // Latitude in degrees + this.lat = +lat; + + // @property lng: Number + // Longitude in degrees + this.lng = +lng; + + // @property alt: Number + // Altitude in meters (optional) + if (alt !== undefined) { + this.alt = +alt; + } +} + +LatLng.prototype = { + // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean + // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (obj, maxMargin) { + if (!obj) { return false; } + + obj = toLatLng(obj); + + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + + return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); + }, + + // @method toString(): String + // Returns a string representation of the point (for debugging purposes). + toString: function (precision) { + return 'LatLng(' + + formatNum(this.lat, precision) + ', ' + + formatNum(this.lng, precision) + ')'; + }, + + // @method distanceTo(otherLatLng: LatLng): Number + // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). + distanceTo: function (other) { + return Earth.distance(this, toLatLng(other)); + }, + + // @method wrap(): LatLng + // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. + wrap: function () { + return Earth.wrapLatLng(this); + }, + + // @method toBounds(sizeInMeters: Number): LatLngBounds + // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. + toBounds: function (sizeInMeters) { + var latAccuracy = 180 * sizeInMeters / 40075017, + lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); + + return toLatLngBounds( + [this.lat - latAccuracy, this.lng - lngAccuracy], + [this.lat + latAccuracy, this.lng + lngAccuracy]); + }, + + clone: function () { + return new LatLng(this.lat, this.lng, this.alt); + } +}; + + + +// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng +// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). + +// @alternative +// @factory L.latLng(coords: Array): LatLng +// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. + +// @alternative +// @factory L.latLng(coords: Object): LatLng +// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. + +function toLatLng(a, b, c) { + if (a instanceof LatLng) { + return a; + } + if (isArray(a) && typeof a[0] !== 'object') { + if (a.length === 3) { + return new LatLng(a[0], a[1], a[2]); + } + if (a.length === 2) { + return new LatLng(a[0], a[1]); + } + return null; + } + if (a === undefined || a === null) { + return a; + } + if (typeof a === 'object' && 'lat' in a) { + return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); + } + if (b === undefined) { + return null; + } + return new LatLng(a, b, c); +} + +/* + * @namespace CRS + * @crs L.CRS.Base + * Object that defines coordinate reference systems for projecting + * geographical points into pixel (screen) coordinates and back (and to + * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See + * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). + * + * Leaflet defines the most usual CRSs by default. If you want to use a + * CRS not defined by default, take a look at the + * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. + * + * Note that the CRS instances do not inherit from Leafet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + */ + +var CRS = { + // @method latLngToPoint(latlng: LatLng, zoom: Number): Point + // Projects geographical coordinates into pixel coordinates for a given zoom. + latLngToPoint: function (latlng, zoom) { + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + // @method pointToLatLng(point: Point, zoom: Number): LatLng + // The inverse of `latLngToPoint`. Projects pixel coordinates on a given + // zoom into geographical coordinates. + pointToLatLng: function (point, zoom) { + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + // @method project(latlng: LatLng): Point + // Projects geographical coordinates into coordinates in units accepted for + // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). + project: function (latlng) { + return this.projection.project(latlng); + }, + + // @method unproject(point: Point): LatLng + // Given a projected coordinate returns the corresponding LatLng. + // The inverse of `project`. + unproject: function (point) { + return this.projection.unproject(point); + }, + + // @method scale(zoom: Number): Number + // Returns the scale used when transforming projected coordinates into + // pixel coordinates for a particular zoom. For example, it returns + // `256 * 2^zoom` for Mercator-based CRS. + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + }, + + // @method zoom(scale: Number): Number + // Inverse of `scale()`, returns the zoom level corresponding to a scale + // factor of `scale`. + zoom: function (scale) { + return Math.log(scale / 256) / Math.LN2; + }, + + // @method getProjectedBounds(zoom: Number): Bounds + // Returns the projection's bounds scaled and transformed for the provided `zoom`. + getProjectedBounds: function (zoom) { + if (this.infinite) { return null; } + + var b = this.projection.bounds, + s = this.scale(zoom), + min = this.transformation.transform(b.min, s), + max = this.transformation.transform(b.max, s); + + return new Bounds(min, max); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates. + + // @property code: String + // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) + // + // @property wrapLng: Number[] + // An array of two numbers defining whether the longitude (horizontal) coordinate + // axis wraps around a given range and how. Defaults to `[-180, 180]` in most + // geographical CRSs. If `undefined`, the longitude axis does not wrap around. + // + // @property wrapLat: Number[] + // Like `wrapLng`, but for the latitude (vertical) axis. + + // wrapLng: [min, max], + // wrapLat: [min, max], + + // @property infinite: Boolean + // If true, the coordinate space will be unbounded (infinite in both axes) + infinite: false, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where lat and lng has been wrapped according to the + // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. + wrapLatLng: function (latlng) { + var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, + lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, + alt = latlng.alt; + + return new LatLng(lat, lng, alt); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring + // that its center is within the CRS's bounds. + // Only accepts actual `L.LatLngBounds` instances, not arrays. + wrapLatLngBounds: function (bounds) { + var center = bounds.getCenter(), + newCenter = this.wrapLatLng(center), + latShift = center.lat - newCenter.lat, + lngShift = center.lng - newCenter.lng; + + if (latShift === 0 && lngShift === 0) { + return bounds; + } + + var sw = bounds.getSouthWest(), + ne = bounds.getNorthEast(), + newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), + newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); + + return new LatLngBounds(newSw, newNe); + } +}; + +/* + * @namespace CRS + * @crs L.CRS.Earth + * + * Serves as the base for CRS that are global such that they cover the earth. + * Can only be used as the base for other CRS and cannot be used directly, + * since it does not have a `code`, `projection` or `transformation`. `distance()` returns + * meters. + */ + +var Earth = extend({}, CRS, { + wrapLng: [-180, 180], + + // Mean Earth Radius, as recommended for use by + // the International Union of Geodesy and Geophysics, + // see http://rosettacode.org/wiki/Haversine_formula + R: 6371000, + + // distance between two geographical points using spherical law of cosines approximation + distance: function (latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), + sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), + a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, + c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return this.R * c; + } +}); + +/* + * @namespace Projection + * @projection L.Projection.SphericalMercator + * + * Spherical Mercator projection — the most common projection for online maps, + * used by almost all free and commercial tile providers. Assumes that Earth is + * a sphere. Used by the `EPSG:3857` CRS. + */ + +var SphericalMercator = { + + R: 6378137, + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { + var d = Math.PI / 180, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + sin = Math.sin(lat * d); + + return new Point( + this.R * latlng.lng * d, + this.R * Math.log((1 + sin) / (1 - sin)) / 2); + }, + + unproject: function (point) { + var d = 180 / Math.PI; + + return new LatLng( + (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, + point.x * d / this.R); + }, + + bounds: (function () { + var d = 6378137 * Math.PI; + return new Bounds([-d, -d], [d, d]); + })() +}; + +/* + * @class Transformation + * @aka L.Transformation + * + * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` + * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing + * the reverse. Used by Leaflet in its projections code. + * + * @example + * + * ```js + * var transformation = L.transformation(2, 5, -1, 10), + * p = L.point(1, 2), + * p2 = transformation.transform(p), // L.point(7, 8) + * p3 = transformation.untransform(p2); // L.point(1, 2) + * ``` + */ + + +// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) +// Creates a `Transformation` object with the given coefficients. +function Transformation(a, b, c, d) { + if (isArray(a)) { + // use array properties + this._a = a[0]; + this._b = a[1]; + this._c = a[2]; + this._d = a[3]; + return; + } + this._a = a; + this._b = b; + this._c = c; + this._d = d; +} + +Transformation.prototype = { + // @method transform(point: Point, scale?: Number): Point + // Returns a transformed point, optionally multiplied by the given scale. + // Only accepts actual `L.Point` instances, not arrays. + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + // @method untransform(point: Point, scale?: Number): Point + // Returns the reverse transformation of the given point, optionally divided + // by the given scale. Only accepts actual `L.Point` instances, not arrays. + untransform: function (point, scale) { + scale = scale || 1; + return new Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } +}; + +// factory L.transformation(a: Number, b: Number, c: Number, d: Number) + +// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) +// Instantiates a Transformation object with the given coefficients. + +// @alternative +// @factory L.transformation(coefficients: Array): Transformation +// Expects an coefficients array of the form +// `[a: Number, b: Number, c: Number, d: Number]`. + +function toTransformation(a, b, c, d) { + return new Transformation(a, b, c, d); +} + +/* + * @namespace CRS + * @crs L.CRS.EPSG3857 + * + * The most common CRS for online maps, used by almost all free and commercial + * tile providers. Uses Spherical Mercator projection. Set in by default in + * Map's `crs` option. + */ + +var EPSG3857 = extend({}, Earth, { + code: 'EPSG:3857', + projection: SphericalMercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * SphericalMercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +var EPSG900913 = extend({}, EPSG3857, { + code: 'EPSG:900913' +}); + +// @namespace SVG; @section +// There are several static functions which can be called without instantiating L.SVG: + +// @function create(name: String): SVGElement +// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), +// corresponding to the class name passed. For example, using 'line' will return +// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). +function svgCreate(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); +} + +// @function pointsToPath(rings: Point[], closed: Boolean): String +// Generates a SVG path string for multiple rings, with each ring turning +// into "M..L..L.." instructions +function pointsToPath(rings, closed) { + var str = '', + i, j, len, len2, points, p; + + for (i = 0, len = rings.length; i < len; i++) { + points = rings[i]; + + for (j = 0, len2 = points.length; j < len2; j++) { + p = points[j]; + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + + // closes the ring for polygons; "x" is VML syntax + str += closed ? (svg ? 'z' : 'x') : ''; + } + + // SVG complains about empty path strings + return str || 'M0 0'; +} + +/* + * @namespace Browser + * @aka L.Browser + * + * A namespace with static properties for browser/feature detection used by Leaflet internally. + * + * @example + * + * ```js + * if (L.Browser.ielt9) { + * alert('Upgrade your browser, dude!'); + * } + * ``` + */ + +var style$1 = document.documentElement.style; + +// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). +var ie = 'ActiveXObject' in window; + +// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. +var ielt9 = ie && !document.addEventListener; + +// @property edge: Boolean; `true` for the Edge web browser. +var edge = 'msLaunchUri' in navigator && !('documentMode' in document); + +// @property webkit: Boolean; +// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). +var webkit = userAgentContains('webkit'); + +// @property android: Boolean +// `true` for any browser running on an Android platform. +var android = userAgentContains('android'); + +// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. +var android23 = userAgentContains('android 2') || userAgentContains('android 3'); + +/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ +var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit +// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) +var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); + +// @property opera: Boolean; `true` for the Opera browser +var opera = !!window.opera; + +// @property chrome: Boolean; `true` for the Chrome browser. +var chrome = userAgentContains('chrome'); + +// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. +var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; + +// @property safari: Boolean; `true` for the Safari browser. +var safari = !chrome && userAgentContains('safari'); + +var phantom = userAgentContains('phantom'); + +// @property opera12: Boolean +// `true` for the Opera browser supporting CSS transforms (version 12 or later). +var opera12 = 'OTransition' in style$1; + +// @property win: Boolean; `true` when the browser is running in a Windows platform +var win = navigator.platform.indexOf('Win') === 0; + +// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. +var ie3d = ie && ('transition' in style$1); + +// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. +var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; + +// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. +var gecko3d = 'MozPerspective' in style$1; + +// @property any3d: Boolean +// `true` for all browsers supporting CSS transforms. +var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; + +// @property mobile: Boolean; `true` for all browsers running in a mobile device. +var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); + +// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. +var mobileWebkit = mobile && webkit; + +// @property mobileWebkit3d: Boolean +// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. +var mobileWebkit3d = mobile && webkit3d; + +// @property msPointer: Boolean +// `true` for browsers implementing the Microsoft touch events model (notably IE10). +var msPointer = !window.PointerEvent && window.MSPointerEvent; + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). +var pointer = !!(window.PointerEvent || msPointer); + +// @property touch: Boolean +// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). +// This does not necessarily mean that the browser is running in a computer with +// a touchscreen, it only means that the browser is capable of understanding +// touch events. +var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || + (window.DocumentTouch && document instanceof window.DocumentTouch)); + +// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. +var mobileOpera = mobile && opera; + +// @property mobileGecko: Boolean +// `true` for gecko-based browsers running in a mobile device. +var mobileGecko = mobile && gecko; + +// @property retina: Boolean +// `true` for browsers on a high-resolution "retina" screen. +var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; + + +// @property canvas: Boolean +// `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). +var canvas = (function () { + return !!document.createElement('canvas').getContext; +}()); + +// @property svg: Boolean +// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). +var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +// @property vml: Boolean +// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). +var vml = !svg && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; + } +}()); + + +function userAgentContains(str) { + return navigator.userAgent.toLowerCase().indexOf(str) >= 0; +} + + +var Browser = (Object.freeze || Object)({ + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + androidStock: androidStock, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + canvas: canvas, + svg: svg, + vml: vml +}); + +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + + +var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; +var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; +var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; +var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; +var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; + +var _pointers = {}; +var _pointerDocListener = false; + +// DomEvent.DoubleTap needs to know about this +var _pointersCount = 0; + +// Provides a touch events wrapper for (ms)pointer events. +// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 + +function addPointerListener(obj, type, handler, id) { + if (type === 'touchstart') { + _addPointerStart(obj, handler, id); + + } else if (type === 'touchmove') { + _addPointerMove(obj, handler, id); + + } else if (type === 'touchend') { + _addPointerEnd(obj, handler, id); + } + + return this; +} + +function removePointerListener(obj, type, id) { + var handler = obj['_leaflet_' + type + id]; + + if (type === 'touchstart') { + obj.removeEventListener(POINTER_DOWN, handler, false); + + } else if (type === 'touchmove') { + obj.removeEventListener(POINTER_MOVE, handler, false); + + } else if (type === 'touchend') { + obj.removeEventListener(POINTER_UP, handler, false); + obj.removeEventListener(POINTER_CANCEL, handler, false); + } + + return this; +} + +function _addPointerStart(obj, handler, id) { + var onDown = bind(function (e) { + if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + // In IE11, some touch events needs to fire for form controls, or + // the controls will stop working. We keep a whitelist of tag names that + // need these events. For other target tags, we prevent default on the event. + if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { + preventDefault(e); + } else { + return; + } + } + + _handlePointer(e, handler); + }); + + obj['_leaflet_touchstart' + id] = onDown; + obj.addEventListener(POINTER_DOWN, onDown, false); + + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen documentElement as any drags that end by moving the touch off the screen get fired there + document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); + document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; + } +} + +function _globalPointerDown(e) { + _pointers[e.pointerId] = e; + _pointersCount++; +} + +function _globalPointerMove(e) { + if (_pointers[e.pointerId]) { + _pointers[e.pointerId] = e; + } +} + +function _globalPointerUp(e) { + delete _pointers[e.pointerId]; + _pointersCount--; +} + +function _handlePointer(e, handler) { + e.touches = []; + for (var i in _pointers) { + e.touches.push(_pointers[i]); + } + e.changedTouches = [e]; + + handler(e); +} + +function _addPointerMove(obj, handler, id) { + var onMove = function (e) { + // don't fire touch moves when mouse isn't down + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } + + _handlePointer(e, handler); + }; + + obj['_leaflet_touchmove' + id] = onMove; + obj.addEventListener(POINTER_MOVE, onMove, false); +} + +function _addPointerEnd(obj, handler, id) { + var onUp = function (e) { + _handlePointer(e, handler); + }; + + obj['_leaflet_touchend' + id] = onUp; + obj.addEventListener(POINTER_UP, onUp, false); + obj.addEventListener(POINTER_CANCEL, onUp, false); +} + +/* + * Extends the event handling code with double tap support for mobile browsers. + */ + +var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; +var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; +var _pre = '_leaflet_'; + +// inspired by Zepto touch code by Thomas Fuchs +function addDoubleTapListener(obj, handler, id) { + var last, touch$$1, + doubleTap = false, + delay = 250; + + function onTouchStart(e) { + var count; + + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + count = _pointersCount; + } else { + count = e.touches.length; + } + + if (count > 1) { return; } + + var now = Date.now(), + delta = now - (last || now); + + touch$$1 = e.touches ? e.touches[0] : e; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } + + function onTouchEnd(e) { + if (doubleTap && !touch$$1.cancelBubble) { + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + // work around .type being readonly with MSPointer* events + var newTouch = {}, + prop, i; + + for (i in touch$$1) { + prop = touch$$1[i]; + newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; + } + touch$$1 = newTouch; + } + touch$$1.type = 'dblclick'; + handler(touch$$1); + last = null; + } + } + + obj[_pre + _touchstart + id] = onTouchStart; + obj[_pre + _touchend + id] = onTouchEnd; + obj[_pre + 'dblclick' + id] = handler; + + obj.addEventListener(_touchstart, onTouchStart, false); + obj.addEventListener(_touchend, onTouchEnd, false); + + // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), + // the browser doesn't fire touchend/pointerup events but does fire + // native dblclicks. See #4127. + // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. + obj.addEventListener('dblclick', handler, false); + + return this; +} + +function removeDoubleTapListener(obj, id) { + var touchstart = obj[_pre + _touchstart + id], + touchend = obj[_pre + _touchend + id], + dblclick = obj[_pre + 'dblclick' + id]; + + obj.removeEventListener(_touchstart, touchstart, false); + obj.removeEventListener(_touchend, touchend, false); + if (!edge) { + obj.removeEventListener('dblclick', dblclick, false); + } + + return this; +} + +/* + * @namespace DomEvent + * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. + */ + +// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. + +// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Adds a listener function (`fn`) to a particular DOM event type of the +// element `el`. You can optionally specify the context of the listener +// (object the `this` keyword will point to). You can also pass several +// space-separated types (e.g. `'click dblclick'`). + +// @alternative +// @function on(el: HTMLElement, eventMap: Object, context?: Object): this +// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` +function on(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + addOne(obj, type, types[type], fn); + } + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + addOne(obj, types[i], fn, context); + } + } + + return this; +} + +var eventsKey = '_leaflet_events'; + +// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Removes a previously added listener function. +// Note that if you passed a custom context to on, you must pass the same +// context to `off` in order to remove the listener. + +// @alternative +// @function off(el: HTMLElement, eventMap: Object, context?: Object): this +// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` +function off(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + removeOne(obj, type, types[type], fn); + } + } else if (types) { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + removeOne(obj, types[i], fn, context); + } + } else { + for (var j in obj[eventsKey]) { + removeOne(obj, j, obj[eventsKey][j]); + } + delete obj[eventsKey]; + } + + return this; +} + +function addOne(obj, type, fn, context) { + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); + + if (obj[eventsKey] && obj[eventsKey][id]) { return this; } + + var handler = function (e) { + return fn.call(context || obj, e || window.event); + }; + + var originalHandler = handler; + + if (pointer && type.indexOf('touch') === 0) { + // Needs DomEvent.Pointer.js + addPointerListener(obj, type, handler, id); + + } else if (touch && (type === 'dblclick') && addDoubleTapListener && + !(pointer && chrome)) { + // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener + // See #5180 + addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + handler = function (e) { + e = e || window.event; + if (isExternalTarget(obj, e)) { + originalHandler(e); + } + }; + obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); + + } else { + if (type === 'click' && android) { + handler = function (e) { + filterClick(e, originalHandler); + }; + } + obj.addEventListener(type, handler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[eventsKey] = obj[eventsKey] || {}; + obj[eventsKey][id] = handler; +} + +function removeOne(obj, type, fn, context) { + + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), + handler = obj[eventsKey] && obj[eventsKey][id]; + + if (!handler) { return this; } + + if (pointer && type.indexOf('touch') === 0) { + removePointerListener(obj, type, id); + + } else if (touch && (type === 'dblclick') && removeDoubleTapListener && + !(pointer && chrome)) { + removeDoubleTapListener(obj, id); + + } else if ('removeEventListener' in obj) { + + if (type === 'mousewheel') { + obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else { + obj.removeEventListener( + type === 'mouseenter' ? 'mouseover' : + type === 'mouseleave' ? 'mouseout' : type, handler, false); + } + + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } + + obj[eventsKey][id] = null; +} + +// @function stopPropagation(ev: DOMEvent): this +// Stop the given event from propagation to parent elements. Used inside the listener functions: +// ```js +// L.DomEvent.on(div, 'click', function (ev) { +// L.DomEvent.stopPropagation(ev); +// }); +// ``` +function stopPropagation(e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else if (e.originalEvent) { // In case of Leaflet event. + e.originalEvent._stopped = true; + } else { + e.cancelBubble = true; + } + skipped(e); + + return this; +} + +// @function disableScrollPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). +function disableScrollPropagation(el) { + addOne(el, 'mousewheel', stopPropagation); + return this; +} + +// @function disableClickPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, +// `'mousedown'` and `'touchstart'` events (plus browser variants). +function disableClickPropagation(el) { + on(el, 'mousedown touchstart dblclick', stopPropagation); + addOne(el, 'click', fakeStop); + return this; +} + +// @function preventDefault(ev: DOMEvent): this +// Prevents the default action of the DOM Event `ev` from happening (such as +// following a link in the href of the a element, or doing a POST request +// with page reload when a `
` is submitted). +// Use it inside listener functions. +function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; +} + +// @function stop(ev: DOMEvent): this +// Does `stopPropagation` and `preventDefault` at the same time. +function stop(e) { + preventDefault(e); + stopPropagation(e); + return this; +} + +// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point +// Gets normalized mouse position from a DOM event relative to the +// `container` or to the whole page if not specified. +function getMousePosition(e, container) { + if (!container) { + return new Point(e.clientX, e.clientY); + } + + var rect = container.getBoundingClientRect(); + + var scaleX = rect.width / container.offsetWidth || 1; + var scaleY = rect.height / container.offsetHeight || 1; + return new Point( + e.clientX / scaleX - rect.left - container.clientLeft, + e.clientY / scaleY - rect.top - container.clientTop); +} + +// Chrome on Win scrolls double the pixels as in other platforms (see #4538), +// and Firefox scrolls device pixels, not CSS pixels +var wheelPxFactor = + (win && chrome) ? 2 * window.devicePixelRatio : + gecko ? window.devicePixelRatio : 1; + +// @function getWheelDelta(ev: DOMEvent): Number +// Gets normalized wheel delta from a mousewheel DOM event, in vertical +// pixels scrolled (negative if scrolling down). +// Events from pointing devices without precise scrolling are mapped to +// a best guess of 60 pixels. +function getWheelDelta(e) { + return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta + (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels + (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines + (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages + (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events + e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels + (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines + e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages + 0; +} + +var skipEvents = {}; + +function fakeStop(e) { + // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) + skipEvents[e.type] = true; +} + +function skipped(e) { + var events = skipEvents[e.type]; + // reset when checking, as it's only used in map container and propagates outside of the map + skipEvents[e.type] = false; + return events; +} + +// check if element really left/entered the event target (for mouseenter/mouseleave) +function isExternalTarget(el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); +} + +var lastClick; + +// this is a horrible workaround for a bug in Android where a single touch triggers two click events +function filterClick(e, handler) { + var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), + elapsed = lastClick && (timeStamp - lastClick); + + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + stop(e); + return; + } + lastClick = timeStamp; + + handler(e); +} + + + + +var DomEvent = (Object.freeze || Object)({ + on: on, + off: off, + stopPropagation: stopPropagation, + disableScrollPropagation: disableScrollPropagation, + disableClickPropagation: disableClickPropagation, + preventDefault: preventDefault, + stop: stop, + getMousePosition: getMousePosition, + getWheelDelta: getWheelDelta, + fakeStop: fakeStop, + skipped: skipped, + isExternalTarget: isExternalTarget, + addListener: on, + removeListener: off +}); + +/* + * @namespace DomUtil + * + * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) + * tree, used by Leaflet internally. + * + * Most functions expecting or returning a `HTMLElement` also work for + * SVG elements. The only difference is that classes refer to CSS classes + * in HTML and SVG classes in SVG. + */ + + +// @property TRANSFORM: String +// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). +var TRANSFORM = testProp( + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + +// @property TRANSITION: String +// Vendor-prefixed transition style name. +var TRANSITION = testProp( + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + +// @property TRANSITION_END: String +// Vendor-prefixed transitionend event name. +var TRANSITION_END = + TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; + + +// @function get(id: String|HTMLElement): HTMLElement +// Returns an element given its DOM id, or returns the element itself +// if it was passed directly. +function get(id) { + return typeof id === 'string' ? document.getElementById(id) : id; +} + +// @function getStyle(el: HTMLElement, styleAttrib: String): String +// Returns the value for a certain style attribute on an element, +// including computed values or values set through CSS. +function getStyle(el, style) { + var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); + + if ((!value || value === 'auto') && document.defaultView) { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + return value === 'auto' ? null : value; +} + +// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement +// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. +function create$1(tagName, className, container) { + var el = document.createElement(tagName); + el.className = className || ''; + + if (container) { + container.appendChild(el); + } + return el; +} + +// @function remove(el: HTMLElement) +// Removes `el` from its parent element +function remove(el) { + var parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } +} + +// @function empty(el: HTMLElement) +// Removes all of `el`'s children elements from `el` +function empty(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} + +// @function toFront(el: HTMLElement) +// Makes `el` the last child of its parent, so it renders in front of the other children. +function toFront(el) { + var parent = el.parentNode; + if (parent.lastChild !== el) { + parent.appendChild(el); + } +} + +// @function toBack(el: HTMLElement) +// Makes `el` the first child of its parent, so it renders behind the other children. +function toBack(el) { + var parent = el.parentNode; + if (parent.firstChild !== el) { + parent.insertBefore(el, parent.firstChild); + } +} + +// @function hasClass(el: HTMLElement, name: String): Boolean +// Returns `true` if the element's class attribute contains `name`. +function hasClass(el, name) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); +} + +// @function addClass(el: HTMLElement, name: String) +// Adds `name` to the element's class attribute. +function addClass(el, name) { + if (el.classList !== undefined) { + var classes = splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!hasClass(el, name)) { + var className = getClass(el); + setClass(el, (className ? className + ' ' : '') + name); + } +} + +// @function removeClass(el: HTMLElement, name: String) +// Removes `name` from the element's class attribute. +function removeClass(el, name) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } +} + +// @function setClass(el: HTMLElement, name: String) +// Sets the element's class. +function setClass(el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } +} + +// @function getClass(el: HTMLElement): String +// Returns the element's class. +function getClass(el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; +} + +// @function setOpacity(el: HTMLElement, opacity: Number) +// Set the opacity of an element (including old IE support). +// `opacity` must be a number from `0` to `1`. +function setOpacity(el, value) { + if ('opacity' in el.style) { + el.style.opacity = value; + } else if ('filter' in el.style) { + _setOpacityIE(el, value); + } +} + +function _setOpacityIE(el, value) { + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } + + value = Math.round(value * 100); + + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } +} + +// @function testProp(props: String[]): String|false +// Goes through the array of style names and returns the first name +// that is a valid style name for an element. If no such name is found, +// it returns false. Useful for vendor-prefixed styles like `transform`. +function testProp(props) { + var style = document.documentElement.style; + + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; + } + } + return false; +} + +// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) +// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels +// and optionally scaled by `scale`. Does not have an effect if the +// browser doesn't support 3D CSS transforms. +function setTransform(el, offset, scale) { + var pos = offset || new Point(0, 0); + + el.style[TRANSFORM] = + (ie3d ? + 'translate(' + pos.x + 'px,' + pos.y + 'px)' : + 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + + (scale ? ' scale(' + scale + ')' : ''); +} + +// @function setPosition(el: HTMLElement, position: Point) +// Sets the position of `el` to coordinates specified by `position`, +// using CSS translate or top/left positioning depending on the browser +// (used by Leaflet internally to position its layers). +function setPosition(el, point) { + + /*eslint-disable */ + el._leaflet_pos = point; + /* eslint-enable */ + + if (any3d) { + setTransform(el, point); + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } +} + +// @function getPosition(el: HTMLElement): Point +// Returns the coordinates of an element previously positioned with setPosition. +function getPosition(el) { + // this method is only used for elements previously positioned using setPosition, + // so it's safe to cache the position for performance + + return el._leaflet_pos || new Point(0, 0); +} + +// @function disableTextSelection() +// Prevents the user from generating `selectstart` DOM events, usually generated +// when the user drags the mouse through a page with text. Used internally +// by Leaflet to override the behaviour of any click-and-drag interaction on +// the map. Affects drag interactions on the whole document. + +// @function enableTextSelection() +// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). +var disableTextSelection; +var enableTextSelection; +var _userSelect; +if ('onselectstart' in document) { + disableTextSelection = function () { + on(window, 'selectstart', preventDefault); + }; + enableTextSelection = function () { + off(window, 'selectstart', preventDefault); + }; +} else { + var userSelectProperty = testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + + disableTextSelection = function () { + if (userSelectProperty) { + var style = document.documentElement.style; + _userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }; + enableTextSelection = function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = _userSelect; + _userSelect = undefined; + } + }; +} + +// @function disableImageDrag() +// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but +// for `dragstart` DOM events, usually generated when the user drags an image. +function disableImageDrag() { + on(window, 'dragstart', preventDefault); +} + +// @function enableImageDrag() +// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). +function enableImageDrag() { + off(window, 'dragstart', preventDefault); +} + +var _outlineElement; +var _outlineStyle; +// @function preventOutline(el: HTMLElement) +// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) +// of the element `el` invisible. Used internally by Leaflet to prevent +// focusable elements from displaying an outline when the user performs a +// drag interaction on them. +function preventOutline(element) { + while (element.tabIndex === -1) { + element = element.parentNode; + } + if (!element.style) { return; } + restoreOutline(); + _outlineElement = element; + _outlineStyle = element.style.outline; + element.style.outline = 'none'; + on(window, 'keydown', restoreOutline); +} + +// @function restoreOutline() +// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). +function restoreOutline() { + if (!_outlineElement) { return; } + _outlineElement.style.outline = _outlineStyle; + _outlineElement = undefined; + _outlineStyle = undefined; + off(window, 'keydown', restoreOutline); +} + + +var DomUtil = (Object.freeze || Object)({ + TRANSFORM: TRANSFORM, + TRANSITION: TRANSITION, + TRANSITION_END: TRANSITION_END, + get: get, + getStyle: getStyle, + create: create$1, + remove: remove, + empty: empty, + toFront: toFront, + toBack: toBack, + hasClass: hasClass, + addClass: addClass, + removeClass: removeClass, + setClass: setClass, + getClass: getClass, + setOpacity: setOpacity, + testProp: testProp, + setTransform: setTransform, + setPosition: setPosition, + getPosition: getPosition, + disableTextSelection: disableTextSelection, + enableTextSelection: enableTextSelection, + disableImageDrag: disableImageDrag, + enableImageDrag: enableImageDrag, + preventOutline: preventOutline, + restoreOutline: restoreOutline +}); + +/* + * @class PosAnimation + * @aka L.PosAnimation + * @inherits Evented + * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. + * + * @example + * ```js + * var fx = new L.PosAnimation(); + * fx.run(el, [300, 500], 0.5); + * ``` + * + * @constructor L.PosAnimation() + * Creates a `PosAnimation` object. + * + */ + +var PosAnimation = Evented.extend({ + + // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) + // Run an animation of a given element to a new position, optionally setting + // duration in seconds (`0.25` by default) and easing linearity factor (3rd + // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), + // `0.5` by default). + run: function (el, newPos, duration, easeLinearity) { + this.stop(); + + this._el = el; + this._inProgress = true; + this._duration = duration || 0.25; + this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); + + this._startPos = getPosition(el); + this._offset = newPos.subtract(this._startPos); + this._startTime = +new Date(); + + // @event start: Event + // Fired when the animation starts + this.fire('start'); + + this._animate(); + }, + + // @method stop() + // Stops the animation (if currently running). + stop: function () { + if (!this._inProgress) { return; } + + this._step(true); + this._complete(); + }, + + _animate: function () { + // animation loop + this._animId = requestAnimFrame(this._animate, this); + this._step(); + }, + + _step: function (round) { + var elapsed = (+new Date()) - this._startTime, + duration = this._duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easeOut(elapsed / duration), round); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (progress, round) { + var pos = this._startPos.add(this._offset.multiplyBy(progress)); + if (round) { + pos._round(); + } + setPosition(this._el, pos); + + // @event step: Event + // Fired continuously during the animation. + this.fire('step'); + }, + + _complete: function () { + cancelAnimFrame(this._animId); + + this._inProgress = false; + // @event end: Event + // Fired when the animation ends. + this.fire('end'); + }, + + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + } +}); + +/* + * @class Map + * @aka L.Map + * @inherits Evented + * + * The central class of the API — it is used to create a map on a page and manipulate it. + * + * @example + * + * ```js + * // initialize the map on the "map" div with a given center and zoom + * var map = L.map('map', { + * center: [51.505, -0.09], + * zoom: 13 + * }); + * ``` + * + */ + +var Map = Evented.extend({ + + options: { + // @section Map State Options + // @option crs: CRS = L.CRS.EPSG3857 + // The [Coordinate Reference System](#crs) to use. Don't change this if you're not + // sure what it means. + crs: EPSG3857, + + // @option center: LatLng = undefined + // Initial geographic center of the map + center: undefined, + + // @option zoom: Number = undefined + // Initial map zoom level + zoom: undefined, + + // @option minZoom: Number = * + // Minimum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the lowest of their `minZoom` options will be used instead. + minZoom: undefined, + + // @option maxZoom: Number = * + // Maximum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the highest of their `maxZoom` options will be used instead. + maxZoom: undefined, + + // @option layers: Layer[] = [] + // Array of layers that will be added to the map initially + layers: [], + + // @option maxBounds: LatLngBounds = null + // When this option is set, the map restricts the view to the given + // geographical bounds, bouncing the user back if the user tries to pan + // outside the view. To set the restriction dynamically, use + // [`setMaxBounds`](#map-setmaxbounds) method. + maxBounds: undefined, + + // @option renderer: Renderer = * + // The default method for drawing vector layers on the map. `L.SVG` + // or `L.Canvas` by default depending on browser support. + renderer: undefined, + + + // @section Animation Options + // @option zoomAnimation: Boolean = true + // Whether the map zoom animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + zoomAnimation: true, + + // @option zoomAnimationThreshold: Number = 4 + // Won't animate zoom if the zoom difference exceeds this value. + zoomAnimationThreshold: 4, + + // @option fadeAnimation: Boolean = true + // Whether the tile fade animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + fadeAnimation: true, + + // @option markerZoomAnimation: Boolean = true + // Whether markers animate their zoom with the zoom animation, if disabled + // they will disappear for the length of the animation. By default it's + // enabled in all browsers that support CSS3 Transitions except Android. + markerZoomAnimation: true, + + // @option transform3DLimit: Number = 2^23 + // Defines the maximum size of a CSS translation transform. The default + // value should not be changed unless a web browser positions layers in + // the wrong place after doing a large `panBy`. + transform3DLimit: 8388608, // Precision limit of a 32-bit float + + // @section Interaction Options + // @option zoomSnap: Number = 1 + // Forces the map's zoom level to always be a multiple of this, particularly + // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. + // By default, the zoom level snaps to the nearest integer; lower values + // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` + // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. + zoomSnap: 1, + + // @option zoomDelta: Number = 1 + // Controls how much the map's zoom level will change after a + // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` + // or `-` on the keyboard, or using the [zoom controls](#control-zoom). + // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. + zoomDelta: 1, + + // @option trackResize: Boolean = true + // Whether the map automatically handles browser window resize to update itself. + trackResize: true + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = setOptions(this, options); + + this._initContainer(id); + this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = bind(this._onResize, this); + + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); + } + + if (options.center && options.zoom !== undefined) { + this.setView(toLatLng(options.center), options.zoom, {reset: true}); + } + + this._handlers = []; + this._layers = {}; + this._zoomBoundLayers = {}; + this._sizeChanged = true; + + this.callInitHooks(); + + // don't animate on browsers without hardware-accelerated transitions or old Android/Opera + this._zoomAnimated = TRANSITION && any3d && !mobileOpera && + this.options.zoomAnimation; + + // zoom transitions run with the same duration for all layers, so if one of transitionend events + // happens after starting zoom animation (propagating to the map pane), we know that it ended globally + if (this._zoomAnimated) { + this._createAnimProxy(); + on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); + } + + this._addLayers(this.options.layers); + }, + + + // @section Methods for modifying map state + + // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) with the given + // animation options. + setView: function (center, zoom, options) { + + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); + options = options || {}; + + this._stop(); + + if (this._loaded && !options.reset && options !== true) { + + if (options.animate !== undefined) { + options.zoom = extend({animate: options.animate}, options.zoom); + options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); + } + + // try animating pan or zoom + var moved = (this._zoom !== zoom) ? + this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : + this._tryAnimatedPan(center, options.pan); + + if (moved) { + // prevent resize handler call, the view will refresh after animation anyway + clearTimeout(this._sizeTimer); + return this; + } + } + + // animation didn't start, just reset the map view + this._resetView(center, zoom); + + return this; + }, + + // @method setZoom(zoom: Number, options?: Zoom/pan options): this + // Sets the zoom of the map. + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = zoom; + return this; + } + return this.setView(this.getCenter(), zoom, {zoom: options}); + }, + + // @method zoomIn(delta?: Number, options?: Zoom options): this + // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomIn: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom + delta, options); + }, + + // @method zoomOut(delta?: Number, options?: Zoom options): this + // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomOut: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom - delta, options); + }, + + // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified geographical point on the map + // stationary (e.g. used internally for scroll zoom and double-click zoom). + // @alternative + // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), + + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); + + return this.setView(newCenter, zoom, {zoom: options}); + }, + + _getBoundsCenterZoom: function (bounds, options) { + + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); + + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), + + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + + zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; + + if (zoom === Infinity) { + return { + center: bounds.getCenter(), + zoom: zoom + }; + } + + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), + + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + + return { + center: center, + zoom: zoom + }; + }, + + // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets a map view that contains the given geographical bounds with the + // maximum zoom level possible. + fitBounds: function (bounds, options) { + + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + throw new Error('Bounds are not valid.'); + } + + var target = this._getBoundsCenterZoom(bounds, options); + return this.setView(target.center, target.zoom, options); + }, + + // @method fitWorld(options?: fitBounds options): this + // Sets a map view that mostly contains the whole world with the maximum + // zoom level possible. + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); + }, + + // @method panTo(latlng: LatLng, options?: Pan options): this + // Pans the map to a given center. + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); + }, + + // @method panBy(offset: Point, options?: Pan options): this + // Pans the map by a given number of pixels (animated). + panBy: function (offset, options) { + offset = toPoint(offset).round(); + options = options || {}; + + if (!offset.x && !offset.y) { + return this.fire('moveend'); + } + // If we pan too far, Chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + return this; + } + + if (!this._panAnim) { + this._panAnim = new PosAnimation(); + + this._panAnim.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } + + // don't fire movestart if animating inertia + if (!options.noMoveStart) { + this.fire('movestart'); + } + + // animate pan unless animate: false specified + if (options.animate !== false) { + addClass(this._mapPane, 'leaflet-pan-anim'); + + var newPos = this._getMapPanePos().subtract(offset).round(); + this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); + } else { + this._rawPanBy(offset); + this.fire('move').fire('moveend'); + } + + return this; + }, + + // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) performing a smooth + // pan-zoom animation. + flyTo: function (targetCenter, targetZoom, options) { + + options = options || {}; + if (options.animate === false || !any3d) { + return this.setView(targetCenter, targetZoom, options); + } + + this._stop(); + + var from = this.project(this.getCenter()), + to = this.project(targetCenter), + size = this.getSize(), + startZoom = this._zoom; + + targetCenter = toLatLng(targetCenter); + targetZoom = targetZoom === undefined ? startZoom : targetZoom; + + var w0 = Math.max(size.x, size.y), + w1 = w0 * this.getZoomScale(startZoom, targetZoom), + u1 = (to.distanceTo(from)) || 1, + rho = 1.42, + rho2 = rho * rho; + + function r(i) { + var s1 = i ? -1 : 1, + s2 = i ? w1 : w0, + t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, + b1 = 2 * s2 * rho2 * u1, + b = t1 / b1, + sq = Math.sqrt(b * b + 1) - b; + + // workaround for floating point precision bug when sq = 0, log = -Infinite, + // thus triggering an infinite loop in flyTo + var log = sq < 0.000000001 ? -18 : Math.log(sq); + + return log; + } + + function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } + function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } + function tanh(n) { return sinh(n) / cosh(n); } + + var r0 = r(0); + + function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } + function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } + + function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } + + var start = Date.now(), + S = (r(1) - r0) / rho, + duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; + + function frame() { + var t = (Date.now() - start) / duration, + s = easeOut(t) * S; + + if (t <= 1) { + this._flyToFrame = requestAnimFrame(frame, this); + + this._move( + this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), + this.getScaleZoom(w0 / w(s), startZoom), + {flyTo: true}); + + } else { + this + ._move(targetCenter, targetZoom) + ._moveEnd(true); + } + } + + this._moveStart(true, options.noMoveStart); + + frame.call(this); + return this; + }, + + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + flyToBounds: function (bounds, options) { + var target = this._getBoundsCenterZoom(bounds, options); + return this.flyTo(target.center, target.zoom, options); + }, + + // @method setMaxBounds(bounds: Bounds): this + // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). + setMaxBounds: function (bounds) { + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + this.options.maxBounds = null; + return this.off('moveend', this._panInsideMaxBounds); + } else if (this.options.maxBounds) { + this.off('moveend', this._panInsideMaxBounds); + } + + this.options.maxBounds = bounds; + + if (this._loaded) { + this._panInsideMaxBounds(); + } + + return this.on('moveend', this._panInsideMaxBounds); + }, + + // @method setMinZoom(zoom: Number): this + // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). + setMinZoom: function (zoom) { + var oldZoom = this.options.minZoom; + this.options.minZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() < this.options.minZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method setMaxZoom(zoom: Number): this + // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). + setMaxZoom: function (zoom) { + var oldZoom = this.options.maxZoom; + this.options.maxZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() > this.options.maxZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this + // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. + panInsideBounds: function (bounds, options) { + this._enforcingBounds = true; + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); + + if (!center.equals(newCenter)) { + this.panTo(newCenter, options); + } + + this._enforcingBounds = false; + return this; + }, + + // @method invalidateSize(options: Zoom/pan options): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. If `options.pan` is `false`, panning will not occur. + // If `options.debounceMoveend` is `true`, it will delay `moveend` event so + // that it doesn't happen often even if the method is called many + // times in a row. + + // @alternative + // @method invalidateSize(animate: Boolean): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. + invalidateSize: function (options) { + if (!this._loaded) { return this; } + + options = extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); + + var oldSize = this.getSize(); + this._sizeChanged = true; + this._lastCenter = null; + + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); + + if (!offset.x && !offset.y) { return this; } + + if (options.animate && options.pan) { + this.panBy(offset); + + } else { + if (options.pan) { + this._rawPanBy(offset); + } + + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } + } + + // @section Map state change events + // @event resize: ResizeEvent + // Fired when the map is resized. + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); + }, + + // @section Methods for modifying map state + // @method stop(): this + // Stops the currently running `panTo` or `flyTo` animation, if any. + stop: function () { + this.setZoom(this._limitZoom(this._zoom)); + if (!this.options.zoomSnap) { + this.fire('viewreset'); + } + return this._stop(); + }, + + // @section Geolocation methods + // @method locate(options?: Locate options): this + // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) + // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, + // and optionally sets the map view to the user's location with respect to + // detection accuracy (or to the world view if geolocation failed). + // Note that, if your page doesn't use HTTPS, this method will fail in + // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) + // See `Locate options` for more details. + locate: function (options) { + + options = this._locateOptions = extend({ + timeout: 10000, + watch: false + // setView: false + // maxZoom: + // maximumAge: 0 + // enableHighAccuracy: false + }, options); + + if (!('geolocation' in navigator)) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = bind(this._handleGeolocationResponse, this), + onError = bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + // @method stopLocate(): this + // Stops watching location previously initiated by `map.locate({watch: true})` + // and aborts resetting the map view if map.locate was called with + // `{setView: true}`. + stopLocate: function () { + if (navigator.geolocation && navigator.geolocation.clearWatch) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; + } + return this; + }, + + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + // @section Location events + // @event locationerror: ErrorEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) failed. + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + }, + + _handleGeolocationResponse: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); + }, + + // TODO Appropriate docs section? + // @section Other Methods + // @method addHandler(name: String, HandlerClass: Function): this + // Adds a new `Handler` to the map, given its name and constructor function. + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } + + var handler = this[name] = new HandlerClass(this); + + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); + } + + return this; + }, + + // @method remove(): this + // Destroys the map and clears all related event listeners. + remove: function () { + + this._initEvents(true); + + if (this._containerId !== this._container._leaflet_id) { + throw new Error('Map container is being reused by another instance'); + } + + try { + // throws error in IE6-8 + delete this._container._leaflet_id; + delete this._containerId; + } catch (e) { + /*eslint-disable */ + this._container._leaflet_id = undefined; + /* eslint-enable */ + this._containerId = undefined; + } + + if (this._locationWatchId !== undefined) { + this.stopLocate(); + } + + this._stop(); + + remove(this._mapPane); + + if (this._clearControlPos) { + this._clearControlPos(); + } + + this._clearHandlers(); + + if (this._loaded) { + // @section Map state change events + // @event unload: Event + // Fired when the map is destroyed with [remove](#map-remove) method. + this.fire('unload'); + } + + var i; + for (i in this._layers) { + this._layers[i].remove(); + } + for (i in this._panes) { + remove(this._panes[i]); + } + + this._layers = []; + this._panes = []; + delete this._mapPane; + delete this._renderer; + + return this; + }, + + // @section Other Methods + // @method createPane(name: String, container?: HTMLElement): HTMLElement + // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, + // then returns it. The pane is created as a child of `container`, or + // as a child of the main map pane if not set. + createPane: function (name, container) { + var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), + pane = create$1('div', className, container || this._mapPane); + + if (name) { + this._panes[name] = pane; + } + return pane; + }, + + // @section Methods for Getting Map State + + // @method getCenter(): LatLng + // Returns the geographical center of the map view + getCenter: function () { + this._checkIfLoaded(); + + if (this._lastCenter && !this._moved()) { + return this._lastCenter; + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + // @method getZoom(): Number + // Returns the current zoom level of the map view + getZoom: function () { + return this._zoom; + }, + + // @method getBounds(): LatLngBounds + // Returns the geographical bounds visible in the current map view + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new LatLngBounds(sw, ne); + }, + + // @method getMinZoom(): Number + // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. + getMinZoom: function () { + return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; + }, + + // @method getMaxZoom(): Number + // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; + }, + + // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number + // Returns the maximum zoom level on which the given bounds fit to the map + // view in its entirety. If `inside` (optional) is set to `true`, the method + // instead returns the minimum zoom level on which the map view fits into + // the given bounds in its entirety. + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = toLatLngBounds(bounds); + padding = toPoint(padding || [0, 0]); + + var zoom = this.getZoom() || 0, + min = this.getMinZoom(), + max = this.getMaxZoom(), + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + size = this.getSize().subtract(padding), + boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + snap = any3d ? this.options.zoomSnap : 1, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); + + zoom = this.getScaleZoom(scale, zoom); + + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; + } + + return Math.max(min, Math.min(max, zoom)); + }, + + // @method getSize(): Point + // Returns the current size of the map container (in pixels). + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new Point( + this._container.clientWidth || 0, + this._container.clientHeight || 0); + + this._sizeChanged = false; + } + return this._size.clone(); + }, + + // @method getPixelBounds(): Bounds + // Returns the bounds of the current map view in projected pixel + // coordinates (sometimes useful in layer and overlay implementations). + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); + return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to + // the map pane? "left point of the map layer" can be confusing, specially + // since there can be negative offsets. + // @method getPixelOrigin(): Point + // Returns the projected pixel coordinates of the top left point of + // the map layer (useful in custom layer and overlay implementations). + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._pixelOrigin; + }, + + // @method getPixelWorldBounds(zoom?: Number): Bounds + // Returns the world's bounds in pixel coordinates for zoom level `zoom`. + // If `zoom` is omitted, the map's current zoom level is used. + getPixelWorldBounds: function (zoom) { + return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); + }, + + // @section Other Methods + + // @method getPane(pane: String|HTMLElement): HTMLElement + // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). + getPane: function (pane) { + return typeof pane === 'string' ? this._panes[pane] : pane; + }, + + // @method getPanes(): Object + // Returns a plain object containing the names of all [panes](#map-pane) as keys and + // the panes as values. + getPanes: function () { + return this._panes; + }, + + // @method getContainer: HTMLElement + // Returns the HTML element that contains the map. + getContainer: function () { + return this._container; + }, + + + // @section Conversion Methods + + // @method getZoomScale(toZoom: Number, fromZoom: Number): Number + // Returns the scale factor to be applied to a map transition from zoom level + // `fromZoom` to `toZoom`. Used internally to help with zoom animations. + getZoomScale: function (toZoom, fromZoom) { + // TODO replace with universal implementation after refactoring projections + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + return crs.scale(toZoom) / crs.scale(fromZoom); + }, + + // @method getScaleZoom(scale: Number, fromZoom: Number): Number + // Returns the zoom level that the map would end up at, if it is at `fromZoom` + // level and everything is scaled by a factor of `scale`. Inverse of + // [`getZoomScale`](#map-getZoomScale). + getScaleZoom: function (scale, fromZoom) { + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + var zoom = crs.zoom(scale * crs.scale(fromZoom)); + return isNaN(zoom) ? Infinity : zoom; + }, + + // @method project(latlng: LatLng, zoom: Number): Point + // Projects a geographical coordinate `LatLng` according to the projection + // of the map's CRS, then scales it according to `zoom` and the CRS's + // `Transformation`. The result is pixel coordinate relative to + // the CRS origin. + project: function (latlng, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); + }, + + // @method unproject(point: Point, zoom: Number): LatLng + // Inverse of [`project`](#map-project). + unproject: function (point, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(toPoint(point), zoom); + }, + + // @method layerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding geographical coordinate (for the current zoom level). + layerPointToLatLng: function (point) { + var projectedPoint = toPoint(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, + + // @method latLngToLayerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the [origin pixel](#map-getpixelorigin). + latLngToLayerPoint: function (latlng) { + var projectedPoint = this.project(toLatLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the + // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the + // CRS's bounds. + // By default this means longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees. + wrapLatLng: function (latlng) { + return this.options.crs.wrapLatLng(toLatLng(latlng)); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring that + // its center is within the CRS's bounds. + // By default this means the center longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees, and the majority of the bounds + // overlaps the CRS's bounds. + wrapLatLngBounds: function (latlng) { + return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates according to + // the map's CRS. By default this measures distance in meters. + distance: function (latlng1, latlng2) { + return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); + }, + + // @method containerPointToLayerPoint(point: Point): Point + // Given a pixel coordinate relative to the map container, returns the corresponding + // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). + containerPointToLayerPoint: function (point) { // (Point) + return toPoint(point).subtract(this._getMapPanePos()); + }, + + // @method layerPointToContainerPoint(point: Point): Point + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding pixel coordinate relative to the map container. + layerPointToContainerPoint: function (point) { // (Point) + return toPoint(point).add(this._getMapPanePos()); + }, + + // @method containerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the map container, returns + // the corresponding geographical coordinate (for the current zoom level). + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(toPoint(point)); + return this.layerPointToLatLng(layerPoint); + }, + + // @method latLngToContainerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the map container. + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + }, + + // @method mouseEventToContainerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to the + // map container where the event took place. + mouseEventToContainerPoint: function (e) { + return getMousePosition(e, this._container); + }, + + // @method mouseEventToLayerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to + // the [origin pixel](#map-getpixelorigin) where the event took place. + mouseEventToLayerPoint: function (e) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + // @method mouseEventToLatLng(ev: MouseEvent): LatLng + // Given a MouseEvent object, returns geographical coordinate where the + // event took place. + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet_id) { + throw new Error('Map container is already initialized.'); + } + + on(container, 'scroll', this._onScroll, this); + this._containerId = stamp(container); + }, + + _initLayout: function () { + var container = this._container; + + this._fadeAnimated = this.options.fadeAnimation && any3d; + + addClass(container, 'leaflet-container' + + (touch ? ' leaflet-touch' : '') + + (retina ? ' leaflet-retina' : '') + + (ielt9 ? ' leaflet-oldie' : '') + + (safari ? ' leaflet-safari' : '') + + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + + var position = getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + this._paneRenderers = {}; + + // @section + // + // Panes are DOM elements used to control the ordering of layers on the map. You + // can access panes with [`map.getPane`](#map-getpane) or + // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the + // [`map.createPane`](#map-createpane) method. + // + // Every map has the following default panes that differ only in zIndex. + // + // @pane mapPane: HTMLElement = 'auto' + // Pane that contains all other map panes + + this._mapPane = this.createPane('mapPane', this._container); + setPosition(this._mapPane, new Point(0, 0)); + + // @pane tilePane: HTMLElement = 200 + // Pane for `GridLayer`s and `TileLayer`s + this.createPane('tilePane'); + // @pane overlayPane: HTMLElement = 400 + // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s + this.createPane('shadowPane'); + // @pane shadowPane: HTMLElement = 500 + // Pane for overlay shadows (e.g. `Marker` shadows) + this.createPane('overlayPane'); + // @pane markerPane: HTMLElement = 600 + // Pane for `Icon`s of `Marker`s + this.createPane('markerPane'); + // @pane tooltipPane: HTMLElement = 650 + // Pane for `Tooltip`s. + this.createPane('tooltipPane'); + // @pane popupPane: HTMLElement = 700 + // Pane for `Popup`s. + this.createPane('popupPane'); + + if (!this.options.markerZoomAnimation) { + addClass(panes.markerPane, 'leaflet-zoom-hide'); + addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + + + // private methods that modify map state + + // @section Map state change events + _resetView: function (center, zoom) { + setPosition(this._mapPane, new Point(0, 0)); + + var loading = !this._loaded; + this._loaded = true; + zoom = this._limitZoom(zoom); + + this.fire('viewprereset'); + + var zoomChanged = this._zoom !== zoom; + this + ._moveStart(zoomChanged, false) + ._move(center, zoom) + ._moveEnd(zoomChanged); + + // @event viewreset: Event + // Fired when the map needs to redraw its content (this usually happens + // on map zoom or load). Very useful for creating custom overlays. + this.fire('viewreset'); + + // @event load: Event + // Fired when the map is initialized (when its center and zoom are set + // for the first time). + if (loading) { + this.fire('load'); + } + }, + + _moveStart: function (zoomChanged, noMoveStart) { + // @event zoomstart: Event + // Fired when the map zoom is about to change (e.g. before zoom animation). + // @event movestart: Event + // Fired when the view of the map starts changing (e.g. user starts dragging the map). + if (zoomChanged) { + this.fire('zoomstart'); + } + if (!noMoveStart) { + this.fire('movestart'); + } + return this; + }, + + _move: function (center, zoom, data) { + if (zoom === undefined) { + zoom = this._zoom; + } + var zoomChanged = this._zoom !== zoom; + + this._zoom = zoom; + this._lastCenter = center; + this._pixelOrigin = this._getNewPixelOrigin(center); + + // @event zoom: Event + // Fired repeatedly during any change in zoom level, including zoom + // and fly animations. + if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } + + // @event move: Event + // Fired repeatedly during any movement of the map, including pan and + // fly animations. + return this.fire('move', data); + }, + + _moveEnd: function (zoomChanged) { + // @event zoomend: Event + // Fired when the map has changed, after any animations. + if (zoomChanged) { + this.fire('zoomend'); + } + + // @event moveend: Event + // Fired when the center of the map stops changing (e.g. user stopped + // dragging the map). + return this.fire('moveend'); + }, + + _stop: function () { + cancelAnimFrame(this._flyToFrame); + if (this._panAnim) { + this._panAnim.stop(); + } + return this; + }, + + _rawPanBy: function (offset) { + setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + + _panInsideMaxBounds: function () { + if (!this._enforcingBounds) { + this.panInsideBounds(this.options.maxBounds); + } + }, + + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, + + // DOM event handling + + // @section Interaction events + _initEvents: function (remove$$1) { + this._targets = {}; + this._targets[stamp(this._container)] = this; + + var onOff = remove$$1 ? off : on; + + // @event click: MouseEvent + // Fired when the user clicks (or taps) the map. + // @event dblclick: MouseEvent + // Fired when the user double-clicks (or double-taps) the map. + // @event mousedown: MouseEvent + // Fired when the user pushes the mouse button on the map. + // @event mouseup: MouseEvent + // Fired when the user releases the mouse button on the map. + // @event mouseover: MouseEvent + // Fired when the mouse enters the map. + // @event mouseout: MouseEvent + // Fired when the mouse leaves the map. + // @event mousemove: MouseEvent + // Fired while the mouse moves over the map. + // @event contextmenu: MouseEvent + // Fired when the user pushes the right mouse button on the map, prevents + // default browser context menu from showing if there are listeners on + // this event. Also fired on mobile when the user holds a single touch + // for a second (also called long press). + // @event keypress: KeyboardEvent + // Fired when the user presses a key from the keyboard while the map is focused. + onOff(this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); + + if (this.options.trackResize) { + onOff(window, 'resize', this._onResize, this); + } + + if (any3d && this.options.transform3DLimit) { + (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); + } + }, + + _onResize: function () { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this); + }, + + _onScroll: function () { + this._container.scrollTop = 0; + this._container.scrollLeft = 0; + }, + + _onMoveEnd: function () { + var pos = this._getMapPanePos(); + if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have + // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ + this._resetView(this.getCenter(), this.getZoom()); + } + }, + + _findEventTargets: function (e, type) { + var targets = [], + target, + isHover = type === 'mouseout' || type === 'mouseover', + src = e.target || e.srcElement, + dragging = false; + + while (src) { + target = this._targets[stamp(src)]; + if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { + // Prevent firing click after you just dragged an object. + dragging = true; + break; + } + if (target && target.listens(type, true)) { + if (isHover && !isExternalTarget(src, e)) { break; } + targets.push(target); + if (isHover) { break; } + } + if (src === this._container) { break; } + src = src.parentNode; + } + if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { + targets = [this]; + } + return targets; + }, + + _handleDOMEvent: function (e) { + if (!this._loaded || skipped(e)) { return; } + + var type = e.type; + + if (type === 'mousedown' || type === 'keypress') { + // prevents outline when clicking on keyboard-focusable element + preventOutline(e.target || e.srcElement); + } + + this._fireDOMEvent(e, type); + }, + + _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], + + _fireDOMEvent: function (e, type, targets) { + + if (e.type === 'click') { + // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). + // @event preclick: MouseEvent + // Fired before mouse click on the map (sometimes useful when you + // want something to happen on click before any existing click + // handlers start running). + var synth = extend({}, e); + synth.type = 'preclick'; + this._fireDOMEvent(synth, synth.type, targets); + } + + if (e._stopped) { return; } + + // Find the layer the event is propagating from and its parents. + targets = (targets || []).concat(this._findEventTargets(e, type)); + + if (!targets.length) { return; } + + var target = targets[0]; + if (type === 'contextmenu' && target.listens(type, true)) { + preventDefault(e); + } + + var data = { + originalEvent: e + }; + + if (e.type !== 'keypress') { + var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); + data.containerPoint = isMarker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + } + + for (var i = 0; i < targets.length; i++) { + targets[i].fire(type, data, true); + if (data.originalEvent._stopped || + (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } + } + }, + + _draggableMoved: function (obj) { + obj = obj.dragging && obj.dragging.enabled() ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } + }, + + // @section Other Methods + + // @method whenReady(fn: Function, context?: Object): this + // Runs the given function `fn` when the map gets initialized with + // a view (center and zoom) and at least one layer, or immediately + // if it's already initialized, optionally passing a function context. + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, {target: this}); + } else { + this.on('load', callback, context); + } + return this; + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return getPosition(this._mapPane) || new Point(0, 0); + }, + + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, + + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); + }, + + _getNewPixelOrigin: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); + }, + + _latLngToNewLayerPoint: function (latlng, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return this.project(latlng, zoom)._subtract(topLeft); + }, + + _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return toBounds([ + this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) + ]); + }, + + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + }, + + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + // If offset is less than a pixel, ignore. + // This prevents unstable projections from getting into + // an infinite loop of tiny offsets. + if (offset.round().equals([0, 0])) { + return center; + } + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var projectedMaxBounds = toBounds( + this.project(maxBounds.getNorthEast(), zoom), + this.project(maxBounds.getSouthWest(), zoom) + ), + minOffset = projectedMaxBounds.min.subtract(pxBounds.min), + maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), + + dx = this._rebound(minOffset.x, -maxOffset.x), + dy = this._rebound(minOffset.y, -maxOffset.y); + + return new Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(), + snap = any3d ? this.options.zoomSnap : 1; + if (snap) { + zoom = Math.round(zoom / snap) * snap; + } + return Math.max(min, Math.min(max, zoom)); + }, + + _onPanTransitionStep: function () { + this.fire('move'); + }, + + _onPanTransitionEnd: function () { + removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, + + _tryAnimatedPan: function (center, options) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._trunc(); + + // don't animate too far unless animate: true specified in options + if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } + + this.panBy(offset, options); + + return true; + }, + + _createAnimProxy: function () { + + var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); + this._panes.mapPane.appendChild(proxy); + + this.on('zoomanim', function (e) { + var prop = TRANSFORM, + transform = this._proxy.style[prop]; + + setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === this._proxy.style[prop] && this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, this); + + this.on('load moveend', function () { + var c = this.getCenter(), + z = this.getZoom(); + setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); + }, this); + + this._on('unload', this._destroyAnimProxy, this); + }, + + _destroyAnimProxy: function () { + remove(this._proxy); + delete this._proxy; + }, + + _catchTransitionEnd: function (e) { + if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + requestAnimFrame(function () { + this + ._moveStart(true, false) + ._animateZoom(center, zoom, true); + }, this); + + return true; + }, + + _animateZoom: function (center, zoom, startAnim, noUpdate) { + if (!this._mapPane) { return; } + + if (startAnim) { + this._animatingZoom = true; + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + addClass(this._mapPane, 'leaflet-zoom-anim'); + } + + // @event zoomanim: ZoomAnimEvent + // Fired on every frame of a zoom animation + this.fire('zoomanim', { + center: center, + zoom: zoom, + noUpdate: noUpdate + }); + + // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 + setTimeout(bind(this._onZoomTransitionEnd, this), 250); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + if (this._mapPane) { + removeClass(this._mapPane, 'leaflet-zoom-anim'); + } + + this._animatingZoom = false; + + this._move(this._animateToCenter, this._animateToZoom); + + // This anim frame should prevent an obscure iOS webkit tile loading race condition. + requestAnimFrame(function () { + this._moveEnd(true); + }, this); + } +}); + +// @section + +// @factory L.map(id: String, options?: Map options) +// Instantiates a map object given the DOM ID of a `
` element +// and optionally an object literal with `Map options`. +// +// @alternative +// @factory L.map(el: HTMLElement, options?: Map options) +// Instantiates a map object given an instance of a `
` HTML element +// and optionally an object literal with `Map options`. +function createMap(id, options) { + return new Map(id, options); +} + +/* + * @class Control + * @aka L.Control + * @inherits Class + * + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ + +var Control = Class.extend({ + // @section + // @aka Control options + options: { + // @option position: String = 'topright' + // The position of the control (one of the map corners). Possible values are `'topleft'`, + // `'topright'`, `'bottomleft'` or `'bottomright'` + position: 'topright' + }, + + initialize: function (options) { + setOptions(this, options); + }, + + /* @section + * Classes extending L.Control will inherit the following methods: + * + * @method getPosition: string + * Returns the position of the control. + */ + getPosition: function () { + return this.options.position; + }, + + // @method setPosition(position: string): this + // Sets the position of the control. + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTMLElement that contains the control. + getContainer: function () { + return this._container; + }, + + // @method addTo(map: Map): this + // Adds the control to the given map. + addTo: function (map) { + this.remove(); + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + // @method remove: this + // Removes the control from the map it is currently active on. + remove: function () { + if (!this._map) { + return this; + } + + remove(this._container); + + if (this.onRemove) { + this.onRemove(this._map); + } + + this._map = null; + + return this; + }, + + _refocusOnMap: function (e) { + // if map exists and event is not a keyboard event + if (this._map && e && e.screenX > 0 && e.screenY > 0) { + this._map.getContainer().focus(); + } + } +}); + +var control = function (options) { + return new Control(options); +}; + +/* @section Extension methods + * @uninheritable + * + * Every control should extend from `L.Control` and (re-)implement the following methods. + * + * @method onAdd(map: Map): HTMLElement + * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). + * + * @method onRemove(map: Map) + * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). + */ + +/* @namespace Map + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addControl(control: Control): this + // Adds the given control to the map + addControl: function (control) { + control.addTo(this); + return this; + }, + + // @method removeControl(control: Control): this + // Removes the given control from the map + removeControl: function (control) { + control.remove(); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + create$1('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = create$1('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + for (var i in this._controlCorners) { + remove(this._controlCorners[i]); + } + remove(this._controlContainer); + delete this._controlCorners; + delete this._controlContainer; + } +}); + +/* + * @class Control.Layers + * @aka L.Control.Layers + * @inherits Control + * + * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. + * + * @example + * + * ```js + * var baseLayers = { + * "Mapbox": mapbox, + * "OpenStreetMap": osm + * }; + * + * var overlays = { + * "Marker": marker, + * "Roads": roadsLayer + * }; + * + * L.control.layers(baseLayers, overlays).addTo(map); + * ``` + * + * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: + * + * ```js + * { + * "": layer1, + * "": layer2 + * } + * ``` + * + * The layer names can contain HTML, which allows you to add additional styling to the items: + * + * ```js + * {" My Layer": myLayer} + * ``` + */ + +var Layers = Control.extend({ + // @section + // @aka Control.Layers options + options: { + // @option collapsed: Boolean = true + // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. + collapsed: true, + position: 'topright', + + // @option autoZIndex: Boolean = true + // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. + autoZIndex: true, + + // @option hideSingleBase: Boolean = false + // If `true`, the base layers in the control will be hidden when there is only one. + hideSingleBase: false, + + // @option sortLayers: Boolean = false + // Whether to sort the layers. When `false`, layers will keep the order + // in which they were added to the control. + sortLayers: false, + + // @option sortFunction: Function = * + // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + // that will be used for sorting the layers, when `sortLayers` is `true`. + // The function receives both the `L.Layer` instances and their names, as in + // `sortFunction(layerA, layerB, nameA, nameB)`. + // By default, it sorts layers alphabetically by their name. + sortFunction: function (layerA, layerB, nameA, nameB) { + return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); + } + }, + + initialize: function (baseLayers, overlays, options) { + setOptions(this, options); + + this._layerControlInputs = []; + this._layers = []; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + this._map = map; + map.on('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.on('add remove', this._onLayerChange, this); + } + + return this._container; + }, + + addTo: function (map) { + Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + + onRemove: function () { + this._map.off('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + }, + + // @method addBaseLayer(layer: Layer, name: String): this + // Adds a base layer (radio button entry) with the given name to the control. + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + return (this._map) ? this._update() : this; + }, + + // @method addOverlay(layer: Layer, name: String): this + // Adds an overlay (checkbox entry) with the given name to the control. + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + return (this._map) ? this._update() : this; + }, + + // @method removeLayer(layer: Layer): this + // Remove the given layer from the control. + removeLayer: function (layer) { + layer.off('add remove', this._onLayerChange, this); + + var obj = this._getLayer(stamp(layer)); + if (obj) { + this._layers.splice(this._layers.indexOf(obj), 1); + } + return (this._map) ? this._update() : this; + }, + + // @method expand(): this + // Expand the control container if collapsed. + expand: function () { + addClass(this._container, 'leaflet-control-layers-expanded'); + this._form.style.height = null; + var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); + if (acceptableHeight < this._form.clientHeight) { + addClass(this._form, 'leaflet-control-layers-scrollbar'); + this._form.style.height = acceptableHeight + 'px'; + } else { + removeClass(this._form, 'leaflet-control-layers-scrollbar'); + } + this._checkDisabledLayers(); + return this; + }, + + // @method collapse(): this + // Collapse the control container if expanded. + collapse: function () { + removeClass(this._container, 'leaflet-control-layers-expanded'); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = create$1('div', className), + collapsed = this.options.collapsed; + + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + disableClickPropagation(container); + disableScrollPropagation(container); + + var form = this._form = create$1('form', className + '-list'); + + if (collapsed) { + this._map.on('click', this.collapse, this); + + if (!android) { + on(container, { + mouseenter: this.expand, + mouseleave: this.collapse + }, this); + } + } + + var link = this._layersLink = create$1('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (touch) { + on(link, 'click', stop); + on(link, 'click', this.expand, this); + } else { + on(link, 'focus', this.expand, this); + } + + if (!collapsed) { + this.expand(); + } + + this._baseLayersList = create$1('div', className + '-base', form); + this._separator = create$1('div', className + '-separator', form); + this._overlaysList = create$1('div', className + '-overlays', form); + + container.appendChild(form); + }, + + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { + + if (this._layers[i] && stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + + _addLayer: function (layer, name, overlay) { + if (this._map) { + layer.on('add remove', this._onLayerChange, this); + } + + this._layers.push({ + layer: layer, + name: name, + overlay: overlay + }); + + if (this.options.sortLayers) { + this._layers.sort(bind(function (a, b) { + return this.options.sortFunction(a.layer, b.layer, a.name, b.name); + }, this)); + } + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + + this._expandIfNotCollapsed(); + }, + + _update: function () { + if (!this._container) { return this; } + + empty(this._baseLayersList); + empty(this._overlaysList); + + this._layerControlInputs = []; + var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; + + for (i = 0; i < this._layers.length; i++) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; + } + + // Hide base layers section if there's only one layer. + if (this.options.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; + + return this; + }, + + _onLayerChange: function (e) { + if (!this._handlingClick) { + this._update(); + } + + var obj = this._getLayer(stamp(e.target)); + + // @namespace Map + // @section Layer events + // @event baselayerchange: LayersControlEvent + // Fired when the base layer is changed through the [layer control](#control-layers). + // @event overlayadd: LayersControlEvent + // Fired when an overlay is selected through the [layer control](#control-layers). + // @event overlayremove: LayersControlEvent + // Fired when an overlay is deselected through the [layer control](#control-layers). + // @namespace Control.Layers + var type = obj.overlay ? + (e.type === 'add' ? 'overlayadd' : 'overlayremove') : + (e.type === 'add' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); + } + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { + + var radioHtml = ''; + + var radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; + + return radioFragment.firstChild; + }, + + _addItem: function (obj) { + var label = document.createElement('label'), + checked = this._map.hasLayer(obj.layer), + input; + + if (obj.overlay) { + input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this._createRadioElement('leaflet-base-layers', checked); + } + + this._layerControlInputs.push(input); + input.layerId = stamp(obj.layer); + + on(input, 'click', this._onInputClick, this); + + var name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + // Helps from preventing layer control flicker when checkboxes are disabled + // https://github.com/Leaflet/Leaflet/issues/2771 + var holder = document.createElement('div'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + var container = obj.overlay ? this._overlaysList : this._baseLayersList; + container.appendChild(label); + + this._checkDisabledLayers(); + return label; + }, + + _onInputClick: function () { + var inputs = this._layerControlInputs, + input, layer; + var addedLayers = [], + removedLayers = []; + + this._handlingClick = true; + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + + if (input.checked) { + addedLayers.push(layer); + } else if (!input.checked) { + removedLayers.push(layer); + } + } + + // Bugfix issue 2318: Should remove all old layers before readding new ones + for (i = 0; i < removedLayers.length; i++) { + if (this._map.hasLayer(removedLayers[i])) { + this._map.removeLayer(removedLayers[i]); + } + } + for (i = 0; i < addedLayers.length; i++) { + if (!this._map.hasLayer(addedLayers[i])) { + this._map.addLayer(addedLayers[i]); + } + } + + this._handlingClick = false; + + this._refocusOnMap(); + }, + + _checkDisabledLayers: function () { + var inputs = this._layerControlInputs, + input, + layer, + zoom = this._map.getZoom(); + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || + (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); + + } + }, + + _expandIfNotCollapsed: function () { + if (this._map && !this.options.collapsed) { + this.expand(); + } + return this; + }, + + _expand: function () { + // Backward compatibility, remove me in 1.1. + return this.expand(); + }, + + _collapse: function () { + // Backward compatibility, remove me in 1.1. + return this.collapse(); + } + +}); + + +// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) +// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. +var layers = function (baseLayers, overlays, options) { + return new Layers(baseLayers, overlays, options); +}; + +/* + * @class Control.Zoom + * @aka L.Control.Zoom + * @inherits Control + * + * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. + */ + +var Zoom = Control.extend({ + // @section + // @aka Control.Zoom options + options: { + position: 'topleft', + + // @option zoomInText: String = '+' + // The text set on the 'zoom in' button. + zoomInText: '+', + + // @option zoomInTitle: String = 'Zoom in' + // The title set on the 'zoom in' button. + zoomInTitle: 'Zoom in', + + // @option zoomOutText: String = '−' + // The text set on the 'zoom out' button. + zoomOutText: '−', + + // @option zoomOutTitle: String = 'Zoom out' + // The title set on the 'zoom out' button. + zoomOutTitle: 'Zoom out' + }, + + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = create$1('div', zoomName + ' leaflet-bar'), + options = this.options; + + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + zoomName + '-in', container, this._zoomIn); + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); + }, + + disable: function () { + this._disabled = true; + this._updateDisabled(); + return this; + }, + + enable: function () { + this._disabled = false; + this._updateDisabled(); + return this; + }, + + _zoomIn: function (e) { + if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { + this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _zoomOut: function (e) { + if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { + this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _createButton: function (html, title, className, container, fn) { + var link = create$1('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + /* + * Will force screen readers like VoiceOver to read this as "Zoom in - button" + */ + link.setAttribute('role', 'button'); + link.setAttribute('aria-label', title); + + disableClickPropagation(link); + on(link, 'click', stop); + on(link, 'click', fn, this); + on(link, 'click', this._refocusOnMap, this); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + removeClass(this._zoomInButton, className); + removeClass(this._zoomOutButton, className); + + if (this._disabled || map._zoom === map.getMinZoom()) { + addClass(this._zoomOutButton, className); + } + if (this._disabled || map._zoom === map.getMaxZoom()) { + addClass(this._zoomInButton, className); + } + } +}); + +// @namespace Map +// @section Control options +// @option zoomControl: Boolean = true +// Whether a [zoom control](#control-zoom) is added to the map by default. +Map.mergeOptions({ + zoomControl: true +}); + +Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new Zoom(); + this.addControl(this.zoomControl); + } +}); + +// @namespace Control.Zoom +// @factory L.control.zoom(options: Control.Zoom options) +// Creates a zoom control +var zoom = function (options) { + return new Zoom(options); +}; + +/* + * @class Control.Scale + * @aka L.Control.Scale + * @inherits Control + * + * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. + * + * @example + * + * ```js + * L.control.scale().addTo(map); + * ``` + */ + +var Scale = Control.extend({ + // @section + // @aka Control.Scale options + options: { + position: 'bottomleft', + + // @option maxWidth: Number = 100 + // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). + maxWidth: 100, + + // @option metric: Boolean = True + // Whether to show the metric scale line (m/km). + metric: true, + + // @option imperial: Boolean = True + // Whether to show the imperial scale line (mi/ft). + imperial: true + + // @option updateWhenIdle: Boolean = false + // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). + }, + + onAdd: function (map) { + var className = 'leaflet-control-scale', + container = create$1('div', className), + options = this.options; + + this._addScales(options, className + '-line', container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = create$1('div', className, container); + } + if (options.imperial) { + this._iScale = create$1('div', className, container); + } + }, + + _update: function () { + var map = this._map, + y = map.getSize().y / 2; + + var maxMeters = map.distance( + map.containerPointToLatLng([0, y]), + map.containerPointToLatLng([this.options.maxWidth, y])); + + this._updateScales(maxMeters); + }, + + _updateScales: function (maxMeters) { + if (this.options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + if (this.options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters), + label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + + this._updateScale(this._mScale, label, meters / maxMeters); + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); + + } else { + feet = this._getRoundNum(maxFeet); + this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); + } + }, + + _updateScale: function (scale, text, ratio) { + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; + scale.innerHTML = text; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : + d >= 5 ? 5 : + d >= 3 ? 3 : + d >= 2 ? 2 : 1; + + return pow10 * d; + } +}); + + +// @factory L.control.scale(options?: Control.Scale options) +// Creates an scale control with the given options. +var scale = function (options) { + return new Scale(options); +}; + +/* + * @class Control.Attribution + * @aka L.Control.Attribution + * @inherits Control + * + * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. + */ + +var Attribution = Control.extend({ + // @section + // @aka Control.Attribution options + options: { + position: 'bottomright', + + // @option prefix: String = 'Leaflet' + // The HTML text shown before the attributions. Pass `false` to disable. + prefix: 'Leaflet' + }, + + initialize: function (options) { + setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + map.attributionControl = this; + this._container = create$1('div', 'leaflet-control-attribution'); + disableClickPropagation(this._container); + + // TODO ugly, refactor + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + this._update(); + + return this._container; + }, + + // @method setPrefix(prefix: String): this + // Sets the text before the attributions. + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + // @method addAttribution(text: String): this + // Adds an attribution text (e.g. `'Vector data © Mapbox'`). + addAttribution: function (text) { + if (!text) { return this; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + // @method removeAttribution(text: String): this + // Removes an attribution text. + removeAttribution: function (text) { + if (!text) { return this; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' | '); + } +}); + +// @namespace Map +// @section Control options +// @option attributionControl: Boolean = true +// Whether a [attribution control](#control-attribution) is added to the map by default. +Map.mergeOptions({ + attributionControl: true +}); + +Map.addInitHook(function () { + if (this.options.attributionControl) { + new Attribution().addTo(this); + } +}); + +// @namespace Control.Attribution +// @factory L.control.attribution(options: Control.Attribution options) +// Creates an attribution control. +var attribution = function (options) { + return new Attribution(options); +}; + +Control.Layers = Layers; +Control.Zoom = Zoom; +Control.Scale = Scale; +Control.Attribution = Attribution; + +control.layers = layers; +control.zoom = zoom; +control.scale = scale; +control.attribution = attribution; + +/* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ + +// @class Handler +// @aka L.Handler +// Abstract class for map interaction handlers + +var Handler = Class.extend({ + initialize: function (map) { + this._map = map; + }, + + // @method enable(): this + // Enables the handler + enable: function () { + if (this._enabled) { return this; } + + this._enabled = true; + this.addHooks(); + return this; + }, + + // @method disable(): this + // Disables the handler + disable: function () { + if (!this._enabled) { return this; } + + this._enabled = false; + this.removeHooks(); + return this; + }, + + // @method enabled(): Boolean + // Returns `true` if the handler is enabled + enabled: function () { + return !!this._enabled; + } + + // @section Extension methods + // Classes inheriting from `Handler` must implement the two following methods: + // @method addHooks() + // Called when the handler is enabled, should add event hooks. + // @method removeHooks() + // Called when the handler is disabled, should remove the event hooks added previously. +}); + +// @section There is static function which can be called without instantiating L.Handler: +// @function addTo(map: Map, name: String): this +// Adds a new Handler to the given map with the given name. +Handler.addTo = function (map, name) { + map.addHandler(name, this); + return this; +}; + +var Mixin = {Events: Events}; + +/* + * @class Draggable + * @aka L.Draggable + * @inherits Evented + * + * A class for making DOM elements draggable (including touch support). + * Used internally for map and marker dragging. Only works for elements + * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). + * + * @example + * ```js + * var draggable = new L.Draggable(elementToDrag); + * draggable.enable(); + * ``` + */ + +var START = touch ? 'touchstart mousedown' : 'mousedown'; +var END = { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend' +}; +var MOVE = { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove' +}; + + +var Draggable = Evented.extend({ + + options: { + // @section + // @aka Draggable options + // @option clickTolerance: Number = 3 + // The max number of pixels a user can shift the mouse pointer during a click + // for it to be considered a valid click (as opposed to a mouse drag). + clickTolerance: 3 + }, + + // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) + // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). + initialize: function (element, dragStartTarget, preventOutline$$1, options) { + setOptions(this, options); + + this._element = element; + this._dragStartTarget = dragStartTarget || element; + this._preventOutline = preventOutline$$1; + }, + + // @method enable() + // Enables the dragging ability + enable: function () { + if (this._enabled) { return; } + + on(this._dragStartTarget, START, this._onDown, this); + + this._enabled = true; + }, + + // @method disable() + // Disables the dragging ability + disable: function () { + if (!this._enabled) { return; } + + // If we're currently dragging this draggable, + // disabling it counts as first ending the drag. + if (Draggable._dragging === this) { + this.finishDrag(); + } + + off(this._dragStartTarget, START, this._onDown, this); + + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + this._moved = false; + + if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } + + if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + Draggable._dragging = this; // Prevent dragging multiple objects at once. + + if (this._preventOutline) { + preventOutline(this._element); + } + + disableImageDrag(); + disableTextSelection(); + + if (this._moving) { return; } + + // @event down: Event + // Fired when a drag is about to start. + this.fire('down'); + + var first = e.touches ? e.touches[0] : e; + + this._startPoint = new Point(first.clientX, first.clientY); + + on(document, MOVE[e.type], this._onMove, this); + on(document, END[e.type], this._onUp, this); + }, + + _onMove: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new Point(first.clientX, first.clientY), + offset = newPoint.subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } + + preventDefault(e); + + if (!this._moved) { + // @event dragstart: Event + // Fired when a drag starts + this.fire('dragstart'); + + this._moved = true; + this._startPos = getPosition(this._element).subtract(offset); + + addClass(document.body, 'leaflet-dragging'); + + this._lastTarget = e.target || e.srcElement; + // IE and Edge do not give the element, so fetch it + // if necessary + if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { + this._lastTarget = this._lastTarget.correspondingUseElement; + } + addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + cancelAnimFrame(this._animRequest); + this._lastEvent = e; + this._animRequest = requestAnimFrame(this._updatePosition, this, true); + }, + + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; + + // @event predrag: Event + // Fired continuously during dragging *before* each corresponding + // update of the element's position. + this.fire('predrag', e); + setPosition(this._element, this._newPos); + + // @event drag: Event + // Fired continuously during dragging. + this.fire('drag', e); + }, + + _onUp: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + this.finishDrag(); + }, + + finishDrag: function () { + removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + for (var i in MOVE) { + off(document, MOVE[i], this._onMove, this); + off(document, END[i], this._onUp, this); + } + + enableImageDrag(); + enableTextSelection(); + + if (this._moved && this._moving) { + // ensure drag is not fired after dragend + cancelAnimFrame(this._animRequest); + + // @event dragend: DragEndEvent + // Fired when the drag ends. + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + Draggable._dragging = false; + } + +}); + +/* + * @namespace LineUtil + * + * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. + */ + +// Simplify polyline with vertex reduction and Douglas-Peucker simplification. +// Improves rendering performance dramatically by lessening the number of points to draw. + +// @function simplify(points: Point[], tolerance: Number): Point[] +// Dramatically reduces the number of points in a polyline while retaining +// its shape and returns a new array of simplified points, using the +// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). +// Used for a huge performance boost when processing/displaying Leaflet polylines for +// each zoom level and also reducing visual noise. tolerance affects the amount of +// simplification (lesser value means higher quality but slower and with more points). +// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). +function simplify(points, tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = _reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = _simplifyDP(points, sqTolerance); + + return points; +} + +// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number +// Returns the distance between point `p` and segment `p1` to `p2`. +function pointToSegmentDistance(p, p1, p2) { + return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); +} + +// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number +// Returns the closest point from a point `p` on a segment `p1` to `p2`. +function closestPointOnSegment(p, p1, p2) { + return _sqClosestPointOnSegment(p, p1, p2); +} + +// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm +function _simplifyDP(points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; +} + +function _simplifyDPStep(points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + _simplifyDPStep(points, markers, sqTolerance, first, index); + _simplifyDPStep(points, markers, sqTolerance, index, last); + } +} + +// reduce points that are too close to each other to a single point +function _reducePoints(points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (_sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; +} + +var _lastCode; + +// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean +// Clips the segment a to b by rectangular bounds with the +// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) +// (modifying the segment points directly!). Used by Leaflet to only show polyline +// points that are on the screen or near, increasing performance. +function clipSegment(a, b, bounds, useLastCode, round) { + var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), + codeB = _getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + _lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + } + + // if a,b is outside the clip window (trivial reject) + if (codeA & codeB) { + return false; + } + + // other cases + codeOut = codeA || codeB; + p = _getEdgeIntersection(a, b, codeOut, bounds, round); + newCode = _getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } +} + +function _getEdgeIntersection(a, b, code, bounds, round) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max, + x, y; + + if (code & 8) { // top + x = a.x + dx * (max.y - a.y) / dy; + y = max.y; + + } else if (code & 4) { // bottom + x = a.x + dx * (min.y - a.y) / dy; + y = min.y; + + } else if (code & 2) { // right + x = max.x; + y = a.y + dy * (max.x - a.x) / dx; + + } else if (code & 1) { // left + x = min.x; + y = a.y + dy * (min.x - a.x) / dx; + } + + return new Point(x, y, round); +} + +function _getBitCode(p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; +} + +// square distance (to avoid unnecessary Math.sqrt calls) +function _sqDist(p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; +} + +// return closest point on segment or distance to that point +function _sqClosestPointOnSegment(p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new Point(x, y); +} + + +// @function isFlat(latlngs: LatLng[]): Boolean +// Returns true if `latlngs` is a flat array, false is nested. +function isFlat(latlngs) { + return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +} + +function _flat(latlngs) { + console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); + return isFlat(latlngs); +} + + +var LineUtil = (Object.freeze || Object)({ + simplify: simplify, + pointToSegmentDistance: pointToSegmentDistance, + closestPointOnSegment: closestPointOnSegment, + clipSegment: clipSegment, + _getEdgeIntersection: _getEdgeIntersection, + _getBitCode: _getBitCode, + _sqClosestPointOnSegment: _sqClosestPointOnSegment, + isFlat: isFlat, + _flat: _flat +}); + +/* + * @namespace PolyUtil + * Various utility functions for polygon geometries. + */ + +/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] + * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). + * Used by Leaflet to only show polygon points that are on the screen or near, increasing + * performance. Note that polygon points needs different algorithm for clipping + * than polyline, so there's a separate method for it. + */ +function clipPolygon(points, bounds, round) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = _getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; +} + + +var PolyUtil = (Object.freeze || Object)({ + clipPolygon: clipPolygon +}); + +/* + * @namespace Projection + * @section + * Leaflet comes with a set of already defined Projections out of the box: + * + * @projection L.Projection.LonLat + * + * Equirectangular, or Plate Carree projection — the most simple projection, + * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as + * latitude. Also suitable for flat worlds, e.g. game maps. Used by the + * `EPSG:4326` and `Simple` CRS. + */ + +var LonLat = { + project: function (latlng) { + return new Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new LatLng(point.y, point.x); + }, + + bounds: new Bounds([-180, -90], [180, 90]) +}; + +/* + * @namespace Projection + * @projection L.Projection.Mercator + * + * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. + */ + +var Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, + + bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + + project: function (latlng) { + var d = Math.PI / 180, + r = this.R, + y = latlng.lat * d, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + con = e * Math.sin(y); + + var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); + y = -r * Math.log(Math.max(ts, 1E-10)); + + return new Point(latlng.lng * d * r, y); + }, + + unproject: function (point) { + var d = 180 / Math.PI, + r = this.R, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + ts = Math.exp(-point.y / r), + phi = Math.PI / 2 - 2 * Math.atan(ts); + + for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { + con = e * Math.sin(phi); + con = Math.pow((1 - con) / (1 + con), e / 2); + dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; + phi += dphi; + } + + return new LatLng(phi * d, point.x * d / r); + } +}; + +/* + * @class Projection + + * An object with methods for projecting geographical coordinates of the world onto + * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). + + * @property bounds: Bounds + * The bounds (specified in CRS units) where the projection is valid + + * @method project(latlng: LatLng): Point + * Projects geographical coordinates into a 2D point. + * Only accepts actual `L.LatLng` instances, not arrays. + + * @method unproject(point: Point): LatLng + * The inverse of `project`. Projects a 2D point into a geographical location. + * Only accepts actual `L.Point` instances, not arrays. + + * Note that the projection instances do not inherit from Leafet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + + */ + + + + +var index = (Object.freeze || Object)({ + LonLat: LonLat, + Mercator: Mercator, + SphericalMercator: SphericalMercator +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG3395 + * + * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + */ +var EPSG3395 = extend({}, Earth, { + code: 'EPSG:3395', + projection: Mercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * Mercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG4326 + * + * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. + * + * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), + * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` + * with this CRS, ensure that there are two 256x256 pixel tiles covering the + * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), + * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. + */ + +var EPSG4326 = extend({}, Earth, { + code: 'EPSG:4326', + projection: LonLat, + transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) +}); + +/* + * @namespace CRS + * @crs L.CRS.Simple + * + * A simple CRS that maps longitude and latitude into `x` and `y` directly. + * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` + * axis should still be inverted (going from bottom to top). `distance()` returns + * simple euclidean distance. + */ + +var Simple = extend({}, CRS, { + projection: LonLat, + transformation: toTransformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + }, + + zoom: function (scale) { + return Math.log(scale) / Math.LN2; + }, + + distance: function (latlng1, latlng2) { + var dx = latlng2.lng - latlng1.lng, + dy = latlng2.lat - latlng1.lat; + + return Math.sqrt(dx * dx + dy * dy); + }, + + infinite: true +}); + +CRS.Earth = Earth; +CRS.EPSG3395 = EPSG3395; +CRS.EPSG3857 = EPSG3857; +CRS.EPSG900913 = EPSG900913; +CRS.EPSG4326 = EPSG4326; +CRS.Simple = Simple; + +/* + * @class Layer + * @inherits Evented + * @aka L.Layer + * @aka ILayer + * + * A set of methods from the Layer base class that all Leaflet layers use. + * Inherits all methods, options and events from `L.Evented`. + * + * @example + * + * ```js + * var layer = L.Marker(latlng).addTo(map); + * layer.addTo(map); + * layer.remove(); + * ``` + * + * @event add: Event + * Fired after the layer is added to a map + * + * @event remove: Event + * Fired after the layer is removed from a map + */ + + +var Layer = Evented.extend({ + + // Classes extending `L.Layer` will inherit the following options: + options: { + // @option pane: String = 'overlayPane' + // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. + pane: 'overlayPane', + + // @option attribution: String = null + // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox". + attribution: null, + + bubblingMouseEvents: true + }, + + /* @section + * Classes extending `L.Layer` will inherit the following methods: + * + * @method addTo(map: Map|LayerGroup): this + * Adds the layer to the given map or layer group. + */ + addTo: function (map) { + map.addLayer(this); + return this; + }, + + // @method remove: this + // Removes the layer from the map it is currently active on. + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); + }, + + // @method removeFrom(map: Map): this + // Removes the layer from the given map + removeFrom: function (obj) { + if (obj) { + obj.removeLayer(this); + } + return this; + }, + + // @method getPane(name? : String): HTMLElement + // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. + getPane: function (name) { + return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); + }, + + addInteractiveTarget: function (targetEl) { + this._map._targets[stamp(targetEl)] = this; + return this; + }, + + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[stamp(targetEl)]; + return this; + }, + + // @method getAttribution: String + // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). + getAttribution: function () { + return this.options.attribution; + }, + + _layerAdd: function (e) { + var map = e.target; + + // check in case layer gets added and then removed before the map is ready + if (!map.hasLayer(this)) { return; } + + this._map = map; + this._zoomAnimated = map._zoomAnimated; + + if (this.getEvents) { + var events = this.getEvents(); + map.on(events, this); + this.once('remove', function () { + map.off(events, this); + }, this); + } + + this.onAdd(map); + + if (this.getAttribution && map.attributionControl) { + map.attributionControl.addAttribution(this.getAttribution()); + } + + this.fire('add'); + map.fire('layeradd', {layer: this}); + } +}); + +/* @section Extension methods + * @uninheritable + * + * Every layer should extend from `L.Layer` and (re-)implement the following methods. + * + * @method onAdd(map: Map): this + * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). + * + * @method onRemove(map: Map): this + * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). + * + * @method getEvents(): Object + * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. + * + * @method getAttribution(): String + * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. + * + * @method beforeAdd(map: Map): this + * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. + */ + + +/* @namespace Map + * @section Layer events + * + * @event layeradd: LayerEvent + * Fired when a new layer is added to the map. + * + * @event layerremove: LayerEvent + * Fired when some layer is removed from the map + * + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addLayer(layer: Layer): this + // Adds the given layer to the map + addLayer: function (layer) { + if (!layer._layerAdd) { + throw new Error('The provided object is not a Layer.'); + } + + var id = stamp(layer); + if (this._layers[id]) { return this; } + this._layers[id] = layer; + + layer._mapToAdd = this; + + if (layer.beforeAdd) { + layer.beforeAdd(this); + } + + this.whenReady(layer._layerAdd, layer); + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the map. + removeLayer: function (layer) { + var id = stamp(layer); + + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); + } + + if (layer.getAttribution && this.attributionControl) { + this.attributionControl.removeAttribution(layer.getAttribution()); + } + + delete this._layers[id]; + + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + layer.fire('remove'); + } + + layer._map = layer._mapToAdd = null; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the map + hasLayer: function (layer) { + return !!layer && (stamp(layer) in this._layers); + }, + + /* @method eachLayer(fn: Function, context?: Object): this + * Iterates over the layers of the map, optionally specifying context of the iterator function. + * ``` + * map.eachLayer(function(layer){ + * layer.bindPopup('Hello'); + * }); + * ``` + */ + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + _addLayers: function (layers) { + layers = layers ? (isArray(layers) ? layers : [layers]) : []; + + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + _addZoomLimit: function (layer) { + if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { + this._zoomBoundLayers[stamp(layer)] = layer; + this._updateZoomLevels(); + } + }, + + _removeZoomLimit: function (layer) { + var id = stamp(layer); + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } + }, + + _updateZoomLevels: function () { + var minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (var i in this._zoomBoundLayers) { + var options = this._zoomBoundLayers[i].options; + + minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); + maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); + } + + this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; + this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; + + // @section Map state change events + // @event zoomlevelschange: Event + // Fired when the number of zoomlevels on the map is changed due + // to adding or removing a layer. + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + + if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { + this.setZoom(this._layersMaxZoom); + } + if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { + this.setZoom(this._layersMinZoom); + } + } +}); + +/* + * @class LayerGroup + * @aka L.LayerGroup + * @inherits Layer + * + * Used to group several layers and handle them as one. If you add it to the map, + * any layers added or removed from the group will be added/removed on the map as + * well. Extends `Layer`. + * + * @example + * + * ```js + * L.layerGroup([marker1, marker2]) + * .addLayer(polyline) + * .addTo(map); + * ``` + */ + +var LayerGroup = Layer.extend({ + + initialize: function (layers, options) { + setOptions(this, options); + + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + // @method addLayer(layer: Layer): this + // Adds the given layer to the group. + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the group. + // @alternative + // @method removeLayer(id: Number): this + // Removes the layer with the given internal ID from the group. + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the group. + // @alternative + // @method hasLayer(id: Number): Boolean + // Returns `true` if the given internal ID is currently added to the group. + hasLayer: function (layer) { + return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); + }, + + // @method clearLayers(): this + // Removes all the layers from the group. + clearLayers: function () { + return this.eachLayer(this.removeLayer, this); + }, + + // @method invoke(methodName: String, …): this + // Calls `methodName` on every layer contained in this group, passing any + // additional parameters. Has no effect if the layers contained do not + // implement `methodName`. + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + this.eachLayer(map.addLayer, map); + }, + + onRemove: function (map) { + this.eachLayer(map.removeLayer, map); + }, + + // @method eachLayer(fn: Function, context?: Object): this + // Iterates over the layers of the group, optionally specifying context of the iterator function. + // ```js + // group.eachLayer(function (layer) { + // layer.bindPopup('Hello'); + // }); + // ``` + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + // @method getLayer(id: Number): Layer + // Returns the layer with the given internal ID. + getLayer: function (id) { + return this._layers[id]; + }, + + // @method getLayers(): Layer[] + // Returns an array of all the layers added to the group. + getLayers: function () { + var layers = []; + this.eachLayer(layers.push, layers); + return layers; + }, + + // @method setZIndex(zIndex: Number): this + // Calls `setZIndex` on every layer contained in this group, passing the z-index. + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + // @method getLayerId(layer: Layer): Number + // Returns the internal ID for a layer + getLayerId: function (layer) { + return stamp(layer); + } +}); + + +// @factory L.layerGroup(layers?: Layer[], options?: Object) +// Create a layer group, optionally given an initial set of layers and an `options` object. +var layerGroup = function (layers, options) { + return new LayerGroup(layers, options); +}; + +/* + * @class FeatureGroup + * @aka L.FeatureGroup + * @inherits LayerGroup + * + * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: + * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) + * * Events are propagated to the `FeatureGroup`, so if the group has an event + * handler, it will handle events from any of the layers. This includes mouse events + * and custom events. + * * Has `layeradd` and `layerremove` events + * + * @example + * + * ```js + * L.featureGroup([marker1, marker2, polyline]) + * .bindPopup('Hello world!') + * .on('click', function() { alert('Clicked on a member of the group!'); }) + * .addTo(map); + * ``` + */ + +var FeatureGroup = LayerGroup.extend({ + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + layer.addEventParent(this); + + LayerGroup.prototype.addLayer.call(this, layer); + + // @event layeradd: LayerEvent + // Fired when a layer is added to this `FeatureGroup` + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + layer.removeEventParent(this); + + LayerGroup.prototype.removeLayer.call(this, layer); + + // @event layerremove: LayerEvent + // Fired when a layer is removed from this `FeatureGroup` + return this.fire('layerremove', {layer: layer}); + }, + + // @method setStyle(style: Path options): this + // Sets the given path options to each layer of the group that has a `setStyle` method. + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + // @method bringToFront(): this + // Brings the layer group to the top of all other layers + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + // @method bringToBack(): this + // Brings the layer group to the back of all other layers + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + // @method getBounds(): LatLngBounds + // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). + getBounds: function () { + var bounds = new LatLngBounds(); + + for (var id in this._layers) { + var layer = this._layers[id]; + bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + } + return bounds; + } +}); + +// @factory L.featureGroup(layers: Layer[]) +// Create a feature group, optionally given an initial set of layers. +var featureGroup = function (layers) { + return new FeatureGroup(layers); +}; + +/* + * @class Icon + * @aka L.Icon + * + * Represents an icon to provide when creating a marker. + * + * @example + * + * ```js + * var myIcon = L.icon({ + * iconUrl: 'my-icon.png', + * iconRetinaUrl: 'my-icon@2x.png', + * iconSize: [38, 95], + * iconAnchor: [22, 94], + * popupAnchor: [-3, -76], + * shadowUrl: 'my-icon-shadow.png', + * shadowRetinaUrl: 'my-icon-shadow@2x.png', + * shadowSize: [68, 95], + * shadowAnchor: [22, 94] + * }); + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. + * + */ + +var Icon = Class.extend({ + + /* @section + * @aka Icon options + * + * @option iconUrl: String = null + * **(required)** The URL to the icon image (absolute or relative to your script path). + * + * @option iconRetinaUrl: String = null + * The URL to a retina sized version of the icon image (absolute or relative to your + * script path). Used for Retina screen devices. + * + * @option iconSize: Point = null + * Size of the icon image in pixels. + * + * @option iconAnchor: Point = null + * The coordinates of the "tip" of the icon (relative to its top left corner). The icon + * will be aligned so that this point is at the marker's geographical location. Centered + * by default if size is specified, also can be set in CSS with negative margins. + * + * @option popupAnchor: Point = [0, 0] + * The coordinates of the point from which popups will "open", relative to the icon anchor. + * + * @option tooltipAnchor: Point = [0, 0] + * The coordinates of the point from which tooltips will "open", relative to the icon anchor. + * + * @option shadowUrl: String = null + * The URL to the icon shadow image. If not specified, no shadow image will be created. + * + * @option shadowRetinaUrl: String = null + * + * @option shadowSize: Point = null + * Size of the shadow image in pixels. + * + * @option shadowAnchor: Point = null + * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same + * as iconAnchor if not specified). + * + * @option className: String = '' + * A custom class name to assign to both icon and shadow images. Empty by default. + */ + + options: { + popupAnchor: [0, 0], + tooltipAnchor: [0, 0], + }, + + initialize: function (options) { + setOptions(this, options); + }, + + // @method createIcon(oldIcon?: HTMLElement): HTMLElement + // Called internally when the icon has to be shown, returns a `` HTML element + // styled according to the options. + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, + + // @method createShadow(oldIcon?: HTMLElement): HTMLElement + // As `createIcon`, but for the shadow beneath it. + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, + + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = toPoint(sizeOption), + anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; + } +}); + + +// @factory L.icon(options: Icon options) +// Creates an icon instance with the given options. +function icon(options) { + return new Icon(options); +} + +/* + * @miniclass Icon.Default (Icon) + * @aka L.Icon.Default + * @section + * + * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when + * no icon is specified. Points to the blue marker image distributed with Leaflet + * releases. + * + * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` + * (which is a set of `Icon options`). + * + * If you want to _completely_ replace the default icon, override the + * `L.Marker.prototype.options.icon` with your own icon instead. + */ + +var IconDefault = Icon.extend({ + + options: { + iconUrl: 'marker-icon.png', + iconRetinaUrl: 'marker-icon-2x.png', + shadowUrl: 'marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] + }, + + _getIconUrl: function (name) { + if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only + IconDefault.imagePath = this._detectIconPath(); + } + + // @option imagePath: String + // `Icon.Default` will try to auto-detect the location of the + // blue icon images. If you are placing these images in a non-standard + // way, set this option to point to the right path. + return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); + }, + + _detectIconPath: function () { + var el = create$1('div', 'leaflet-default-icon-path', document.body); + var path = getStyle(el, 'background-image') || + getStyle(el, 'backgroundImage'); // IE8 + + document.body.removeChild(el); + + if (path === null || path.indexOf('url') !== 0) { + path = ''; + } else { + path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, ''); + } + + return path; + } +}); + +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + + +/* @namespace Marker + * @section Interaction handlers + * + * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: + * + * ```js + * marker.dragging.disable(); + * ``` + * + * @property dragging: Handler + * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). + */ + +var MarkerDrag = Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + + if (!this._draggable) { + this._draggable = new Draggable(icon, icon, true); + } + + this._draggable.on({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).enable(); + + addClass(icon, 'leaflet-marker-draggable'); + }, + + removeHooks: function () { + this._draggable.off({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).disable(); + + if (this._marker._icon) { + removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _adjustPan: function (e) { + var marker = this._marker, + map = marker._map, + speed = this._marker.options.autoPanSpeed, + padding = this._marker.options.autoPanPadding, + iconPos = L.DomUtil.getPosition(marker._icon), + bounds = map.getPixelBounds(), + origin = map.getPixelOrigin(); + + var panBounds = toBounds( + bounds.min._subtract(origin).add(padding), + bounds.max._subtract(origin).subtract(padding) + ); + + if (!panBounds.contains(iconPos)) { + // Compute incremental movement + var movement = toPoint( + (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - + (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), + + (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - + (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) + ).multiplyBy(speed); + + map.panBy(movement, {animate: false}); + + this._draggable._newPos._add(movement); + this._draggable._startPos._add(movement); + + L.DomUtil.setPosition(marker._icon, this._draggable._newPos); + this._onDrag(e); + + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDragStart: function () { + // @section Dragging events + // @event dragstart: Event + // Fired when the user starts dragging the marker. + + // @event movestart: Event + // Fired when the marker starts moving (because of dragging). + + this._oldLatLng = this._marker.getLatLng(); + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, + + _onPreDrag: function (e) { + if (this._marker.options.autoPan) { + cancelAnimFrame(this._panRequest); + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDrag: function (e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + + // update shadow position + if (shadow) { + setPosition(shadow, iconPos); + } + + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + }, + + _onDragEnd: function (e) { + // @event dragend: DragEndEvent + // Fired when the user stops dragging the marker. + + cancelAnimFrame(this._panRequest); + + // @event moveend: Event + // Fired when the marker stops moving (because of dragging). + delete this._oldLatLng; + this._marker + .fire('moveend') + .fire('dragend', e); + } +}); + +/* + * @class Marker + * @inherits Interactive layer + * @aka L.Marker + * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. + * + * @example + * + * ```js + * L.marker([50.5, 30.5]).addTo(map); + * ``` + */ + +var Marker = Layer.extend({ + + // @section + // @aka Marker options + options: { + // @option icon: Icon = * + // Icon instance to use for rendering the marker. + // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. + // If not specified, a common instance of `L.Icon.Default` is used. + icon: new IconDefault(), + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option draggable: Boolean = false + // Whether the marker is draggable with mouse/touch or not. + draggable: false, + + // @option autoPan: Boolean = false + // Set it to `true` if you want the map to do panning animation when marker hits the edges. + autoPan: false, + + // @option autoPanPadding: Point = Point(50, 50) + // Equivalent of setting both top left and bottom right autopan padding to the same value. + autoPanPadding: [50, 50], + + // @option autoPanSpeed: Number = 10 + // Number of pixels the map should move by. + autoPanSpeed: 10, + + // @option keyboard: Boolean = true + // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. + keyboard: true, + + // @option title: String = '' + // Text for the browser tooltip that appear on marker hover (no tooltip by default). + title: '', + + // @option alt: String = '' + // Text for the `alt` attribute of the icon image (useful for accessibility). + alt: '', + + // @option zIndexOffset: Number = 0 + // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). + zIndexOffset: 0, + + // @option opacity: Number = 1.0 + // The opacity of the marker. + opacity: 1, + + // @option riseOnHover: Boolean = false + // If `true`, the marker will get on top of others when you hover the mouse over it. + riseOnHover: false, + + // @option riseOffset: Number = 250 + // The z-index offset used for the `riseOnHover` feature. + riseOffset: 250, + + // @option pane: String = 'markerPane' + // `Map pane` where the markers icon will be added. + pane: 'markerPane', + + // @option bubblingMouseEvents: Boolean = false + // When `true`, a mouse event on this marker will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: false + }, + + /* @section + * + * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: + */ + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + }, + + onAdd: function (map) { + this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; + + if (this._zoomAnimated) { + map.on('zoomanim', this._animateZoom, this); + } + + this._initIcon(); + this.update(); + }, + + onRemove: function (map) { + if (this.dragging && this.dragging.enabled()) { + this.options.draggable = true; + this.dragging.removeHooks(); + } + delete this.dragging; + + if (this._zoomAnimated) { + map.off('zoomanim', this._animateZoom, this); + } + + this._removeIcon(); + this._removeShadow(); + }, + + getEvents: function () { + return { + zoom: this.update, + viewreset: this.update + }; + }, + + // @method getLatLng: LatLng + // Returns the current geographical position of the marker. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Changes the marker position to the given point. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.update(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method setZIndexOffset(offset: Number): this + // Changes the [zIndex offset](#marker-zindexoffset) of the marker. + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + return this.update(); + }, + + // @method setIcon(icon: Icon): this + // Changes the marker icon. + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup, this._popup.options); + } + + return this; + }, + + getElement: function () { + return this._icon; + }, + + update: function () { + + if (this._icon && this._map) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } + + return this; + }, + + _initIcon: function () { + var options = this.options, + classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + + if (icon.tagName === 'IMG') { + icon.alt = options.alt || ''; + } + } + + addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + } + + this._icon = icon; + + if (options.riseOnHover) { + this.on({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + addClass(newShadow, classToAdd); + newShadow.alt = ''; + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + if (addIcon) { + this.getPane().appendChild(this._icon); + } + this._initInteraction(); + if (newShadow && addShadow) { + this.getPane('shadowPane').appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + this.off({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + remove(this._icon); + this.removeInteractiveTarget(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + remove(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + setPosition(this._icon, pos); + + if (this._shadow) { + setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + this._icon.style.zIndex = this._zIndex + offset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.interactive) { return; } + + addClass(this._icon, 'leaflet-interactive'); + + this.addInteractiveTarget(this._icon); + + if (MarkerDrag) { + var draggable = this.options.draggable; + if (this.dragging) { + draggable = this.dragging.enabled(); + this.dragging.disable(); + } + + this.dragging = new MarkerDrag(this); + + if (draggable) { + this.dragging.enable(); + } + } + }, + + // @method setOpacity(opacity: Number): this + // Changes the opacity of the marker. + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + var opacity = this.options.opacity; + + setOpacity(this._icon, opacity); + + if (this._shadow) { + setOpacity(this._shadow, opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + }, + + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor; + }, + + _getTooltipAnchor: function () { + return this.options.icon.options.tooltipAnchor; + } +}); + + +// factory L.marker(latlng: LatLng, options? : Marker options) + +// @factory L.marker(latlng: LatLng, options? : Marker options) +// Instantiates a Marker object given a geographical point and optionally an options object. +function marker(latlng, options) { + return new Marker(latlng, options); +} + +/* + * @class Path + * @aka L.Path + * @inherits Interactive layer + * + * An abstract class that contains options and constants shared between vector + * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. + */ + +var Path = Layer.extend({ + + // @section + // @aka Path options + options: { + // @option stroke: Boolean = true + // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. + stroke: true, + + // @option color: String = '#3388ff' + // Stroke color + color: '#3388ff', + + // @option weight: Number = 3 + // Stroke width in pixels + weight: 3, + + // @option opacity: Number = 1.0 + // Stroke opacity + opacity: 1, + + // @option lineCap: String= 'round' + // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. + lineCap: 'round', + + // @option lineJoin: String = 'round' + // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. + lineJoin: 'round', + + // @option dashArray: String = null + // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashArray: null, + + // @option dashOffset: String = null + // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashOffset: null, + + // @option fill: Boolean = depends + // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. + fill: false, + + // @option fillColor: String = * + // Fill color. Defaults to the value of the [`color`](#path-color) option + fillColor: null, + + // @option fillOpacity: Number = 0.2 + // Fill opacity. + fillOpacity: 0.2, + + // @option fillRule: String = 'evenodd' + // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. + fillRule: 'evenodd', + + // className: '', + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option bubblingMouseEvents: Boolean = true + // When `true`, a mouse event on this path will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: true + }, + + beforeAdd: function (map) { + // Renderer is set here because we need to call renderer.getEvents + // before this.getEvents. + this._renderer = map.getRenderer(this); + }, + + onAdd: function () { + this._renderer._initPath(this); + this._reset(); + this._renderer._addPath(this); + }, + + onRemove: function () { + this._renderer._removePath(this); + }, + + // @method redraw(): this + // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. + redraw: function () { + if (this._map) { + this._renderer._updatePath(this); + } + return this; + }, + + // @method setStyle(style: Path options): this + // Changes the appearance of a Path based on the options in the `Path options` object. + setStyle: function (style) { + setOptions(this, style); + if (this._renderer) { + this._renderer._updateStyle(this); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all path layers. + bringToFront: function () { + if (this._renderer) { + this._renderer._bringToFront(this); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all path layers. + bringToBack: function () { + if (this._renderer) { + this._renderer._bringToBack(this); + } + return this; + }, + + getElement: function () { + return this._path; + }, + + _reset: function () { + // defined in child classes + this._project(); + this._update(); + }, + + _clickTolerance: function () { + // used when doing hit detection for Canvas layers + return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance; + } +}); + +/* + * @class CircleMarker + * @aka L.CircleMarker + * @inherits Path + * + * A circle of a fixed size with radius specified in pixels. Extends `Path`. + */ + +var CircleMarker = Path.extend({ + + // @section + // @aka CircleMarker options + options: { + fill: true, + + // @option radius: Number = 10 + // Radius of the circle marker, in pixels + radius: 10 + }, + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + this._radius = this.options.radius; + }, + + // @method setLatLng(latLng: LatLng): this + // Sets the position of a circle marker to a new location. + setLatLng: function (latlng) { + this._latlng = toLatLng(latlng); + this.redraw(); + return this.fire('move', {latlng: this._latlng}); + }, + + // @method getLatLng(): LatLng + // Returns the current geographical position of the circle marker + getLatLng: function () { + return this._latlng; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle marker. Units are in pixels. + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of the circle + getRadius: function () { + return this._radius; + }, + + setStyle : function (options) { + var radius = options && options.radius || this._radius; + Path.prototype.setStyle.call(this, options); + this.setRadius(radius); + return this; + }, + + _project: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + this._updateBounds(); + }, + + _updateBounds: function () { + var r = this._radius, + r2 = this._radiusY || r, + w = this._clickTolerance(), + p = [r + w, r2 + w]; + this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); + }, + + _update: function () { + if (this._map) { + this._updatePath(); + } + }, + + _updatePath: function () { + this._renderer._updateCircle(this); + }, + + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); + } +}); + + +// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) +// Instantiates a circle marker object given a geographical point, and an optional options object. +function circleMarker(latlng, options) { + return new CircleMarker(latlng, options); +} + +/* + * @class Circle + * @aka L.Circle + * @inherits CircleMarker + * + * A class for drawing circle overlays on a map. Extends `CircleMarker`. + * + * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). + * + * @example + * + * ```js + * L.circle([50.5, 30.5], {radius: 200}).addTo(map); + * ``` + */ + +var Circle = CircleMarker.extend({ + + initialize: function (latlng, options, legacyOptions) { + if (typeof options === 'number') { + // Backwards compatibility with 0.7.x factory (latlng, radius, options?) + options = extend({}, legacyOptions, {radius: options}); + } + setOptions(this, options); + this._latlng = toLatLng(latlng); + + if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } + + // @section + // @aka Circle options + // @option radius: Number; Radius of the circle, in meters. + this._mRadius = this.options.radius; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle. Units are in meters. + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of a circle. Units are in meters. + getRadius: function () { + return this._mRadius; + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + var half = [this._radius, this._radiusY || this._radius]; + + return new LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(half)), + this._map.layerPointToLatLng(this._point.add(half))); + }, + + setStyle: Path.prototype.setStyle, + + _project: function () { + + var lng = this._latlng.lng, + lat = this._latlng.lat, + map = this._map, + crs = map.options.crs; + + if (crs.distance === Earth.distance) { + var d = Math.PI / 180, + latR = (this._mRadius / Earth.R) / d, + top = map.project([lat + latR, lng]), + bottom = map.project([lat - latR, lng]), + p = top.add(bottom).divideBy(2), + lat2 = map.unproject(p).lat, + lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / + (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; + + if (isNaN(lngR) || lngR === 0) { + lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 + } + + this._point = p.subtract(map.getPixelOrigin()); + this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; + this._radiusY = p.y - top.y; + + } else { + var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); + + this._point = map.latLngToLayerPoint(this._latlng); + this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; + } + + this._updateBounds(); + } +}); + +// @factory L.circle(latlng: LatLng, options?: Circle options) +// Instantiates a circle object given a geographical point, and an options object +// which contains the circle radius. +// @alternative +// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) +// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. +// Do not use in new applications or plugins. +function circle(latlng, options, legacyOptions) { + return new Circle(latlng, options, legacyOptions); +} + +/* + * @class Polyline + * @aka L.Polyline + * @inherits Path + * + * A class for drawing polyline overlays on a map. Extends `Path`. + * + * @example + * + * ```js + * // create a red polyline from an array of LatLng points + * var latlngs = [ + * [45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2] + * ]; + * + * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polyline + * map.fitBounds(polyline.getBounds()); + * ``` + * + * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * + * ```js + * // create a red polyline from an array of arrays of LatLng points + * var latlngs = [ + * [[45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2]], + * [[40.78, -73.91], + * [41.83, -87.62], + * [32.76, -96.72]] + * ]; + * ``` + */ + + +var Polyline = Path.extend({ + + // @section + // @aka Polyline options + options: { + // @option smoothFactor: Number = 1.0 + // How much to simplify the polyline on each zoom level. More means + // better performance and smoother look, and less means more accurate representation. + smoothFactor: 1.0, + + // @option noClip: Boolean = false + // Disable polyline clipping. + noClip: false + }, + + initialize: function (latlngs, options) { + setOptions(this, options); + this._setLatLngs(latlngs); + }, + + // @method getLatLngs(): LatLng[] + // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. + getLatLngs: function () { + return this._latlngs; + }, + + // @method setLatLngs(latlngs: LatLng[]): this + // Replaces all the points in the polyline with the given array of geographical points. + setLatLngs: function (latlngs) { + this._setLatLngs(latlngs); + return this.redraw(); + }, + + // @method isEmpty(): Boolean + // Returns `true` if the Polyline has no LatLngs. + isEmpty: function () { + return !this._latlngs.length; + }, + + // @method closestLayerPoint: Point + // Returns the point closest to `p` on the Polyline. + closestLayerPoint: function (p) { + var minDistance = Infinity, + minPoint = null, + closest = _sqClosestPointOnSegment, + p1, p2; + + for (var j = 0, jLen = this._parts.length; j < jLen; j++) { + var points = this._parts[j]; + + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + + var sqDist = closest(p, p1, p2, true); + + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = closest(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + // @method getCenter(): LatLng + // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, halfDist, segDist, dist, p1, p2, ratio, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polyline centroid algorithm; only uses the first ring if there are multiple + + for (i = 0, halfDist = 0; i < len - 1; i++) { + halfDist += points[i].distanceTo(points[i + 1]) / 2; + } + + // The line is so small in the current view that all points are on the same pixel. + if (halfDist === 0) { + return this._map.layerPointToLatLng(points[0]); + } + + for (i = 0, dist = 0; i < len - 1; i++) { + p1 = points[i]; + p2 = points[i + 1]; + segDist = p1.distanceTo(p2); + dist += segDist; + + if (dist > halfDist) { + ratio = (dist - halfDist) / segDist; + return this._map.layerPointToLatLng([ + p2.x - ratio * (p2.x - p1.x), + p2.y - ratio * (p2.y - p1.y) + ]); + } + } + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + return this._bounds; + }, + + // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this + // Adds a given point to the polyline. By default, adds to the first ring of + // the polyline in case of a multi-polyline, but can be overridden by passing + // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). + addLatLng: function (latlng, latlngs) { + latlngs = latlngs || this._defaultShape(); + latlng = toLatLng(latlng); + latlngs.push(latlng); + this._bounds.extend(latlng); + return this.redraw(); + }, + + _setLatLngs: function (latlngs) { + this._bounds = new LatLngBounds(); + this._latlngs = this._convertLatLngs(latlngs); + }, + + _defaultShape: function () { + return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way + _convertLatLngs: function (latlngs) { + var result = [], + flat = isFlat(latlngs); + + for (var i = 0, len = latlngs.length; i < len; i++) { + if (flat) { + result[i] = toLatLng(latlngs[i]); + this._bounds.extend(result[i]); + } else { + result[i] = this._convertLatLngs(latlngs[i]); + } + } + + return result; + }, + + _project: function () { + var pxBounds = new Bounds(); + this._rings = []; + this._projectLatlngs(this._latlngs, this._rings, pxBounds); + + var w = this._clickTolerance(), + p = new Point(w, w); + + if (this._bounds.isValid() && pxBounds.isValid()) { + pxBounds.min._subtract(p); + pxBounds.max._add(p); + this._pxBounds = pxBounds; + } + }, + + // recursively turns latlngs into a set of rings with projected coordinates + _projectLatlngs: function (latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof LatLng, + len = latlngs.length, + i, ring; + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + ring[i] = this._map.latLngToLayerPoint(latlngs[i]); + projectedBounds.extend(ring[i]); + } + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); + } + } + }, + + // clip polyline by renderer bounds so that we have less to render for performance + _clipPoints: function () { + var bounds = this._renderer._bounds; + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + var parts = this._parts, + i, j, k, len, len2, segment, points; + + for (i = 0, k = 0, len = this._rings.length; i < len; i++) { + points = this._rings[i]; + + for (j = 0, len2 = points.length; j < len2 - 1; j++) { + segment = clipSegment(points[j], points[j + 1], bounds, j, true); + + if (!segment) { continue; } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { + parts[k].push(segment[1]); + k++; + } + } + } + }, + + // simplify each clipped part of the polyline for performance + _simplifyPoints: function () { + var parts = this._parts, + tolerance = this.options.smoothFactor; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = simplify(parts[i], tolerance); + } + }, + + _update: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + this._updatePath(); + }, + + _updatePath: function () { + this._renderer._updatePoly(this); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p, closed) { + var i, j, k, len, len2, part, + w = this._clickTolerance(); + + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } + + // hit detection for polylines + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { continue; } + + if (pointToSegmentDistance(p, part[k], part[j]) <= w) { + return true; + } + } + } + return false; + } +}); + +// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) +// Instantiates a polyline object given an array of geographical points and +// optionally an options object. You can create a `Polyline` object with +// multiple separate lines (`MultiPolyline`) by passing an array of arrays +// of geographic points. +function polyline(latlngs, options) { + return new Polyline(latlngs, options); +} + +// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. +Polyline._flat = _flat; + +/* + * @class Polygon + * @aka L.Polygon + * @inherits Polyline + * + * A class for drawing polygon overlays on a map. Extends `Polyline`. + * + * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. + * + * + * @example + * + * ```js + * // create a red polygon from an array of LatLng points + * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; + * + * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polygon + * map.fitBounds(polygon.getBounds()); + * ``` + * + * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: + * + * ```js + * var latlngs = [ + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ]; + * ``` + * + * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. + * + * ```js + * var latlngs = [ + * [ // first polygon + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ], + * [ // second polygon + * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] + * ] + * ]; + * ``` + */ + +var Polygon = Polyline.extend({ + + options: { + fill: true + }, + + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, + + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, j, p1, p2, f, area, x, y, center, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polygon centroid algorithm; only uses the first ring if there are multiple + + area = x = y = 0; + + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; + + f = p1.y * p2.x - p2.y * p1.x; + x += (p1.x + p2.x) * f; + y += (p1.y + p2.y) * f; + area += f * 3; + } + + if (area === 0) { + // Polygon is so small that all points are on same pixel. + center = points[0]; + } else { + center = [x / area, y / area]; + } + return this._map.layerPointToLatLng(center); + }, + + _convertLatLngs: function (latlngs) { + var result = Polyline.prototype._convertLatLngs.call(this, latlngs), + len = result.length; + + // remove last point if it equals first one + if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { + result.pop(); + } + return result; + }, + + _setLatLngs: function (latlngs) { + Polyline.prototype._setLatLngs.call(this, latlngs); + if (isFlat(this._latlngs)) { + this._latlngs = [this._latlngs]; + } + }, + + _defaultShape: function () { + return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, + + _clipPoints: function () { + // polygons need a different clipping algorithm so we redefine that + + var bounds = this._renderer._bounds, + w = this.options.weight, + p = new Point(w, w); + + // increase clip padding by stroke width to avoid stroke on clip edges + bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + for (var i = 0, len = this._rings.length, clipped; i < len; i++) { + clipped = clipPolygon(this._rings[i], bounds, true); + if (clipped.length) { + this._parts.push(clipped); + } + } + }, + + _updatePath: function () { + this._renderer._updatePoly(this, true); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + var inside = false, + part, p1, p2, i, j, k, len, len2; + + if (!this._pxBounds.contains(p)) { return false; } + + // ray casting algorithm for detecting if point is in polygon + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + // also check if it's on polygon stroke + return inside || Polyline.prototype._containsPoint.call(this, p, true); + } + +}); + + +// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) +function polygon(latlngs, options) { + return new Polygon(latlngs, options); +} + +/* + * @class GeoJSON + * @aka L.GeoJSON + * @inherits FeatureGroup + * + * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse + * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * + * @example + * + * ```js + * L.geoJSON(data, { + * style: function (feature) { + * return {color: feature.properties.color}; + * } + * }).bindPopup(function (layer) { + * return layer.feature.properties.description; + * }).addTo(map); + * ``` + */ + +var GeoJSON = FeatureGroup.extend({ + + /* @section + * @aka GeoJSON options + * + * @option pointToLayer: Function = * + * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally + * called when data is added, passing the GeoJSON point feature and its `LatLng`. + * The default is to spawn a default `Marker`: + * ```js + * function(geoJsonPoint, latlng) { + * return L.marker(latlng); + * } + * ``` + * + * @option style: Function = * + * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, + * called internally when data is added. + * The default value is to not override any defaults: + * ```js + * function (geoJsonFeature) { + * return {} + * } + * ``` + * + * @option onEachFeature: Function = * + * A `Function` that will be called once for each created `Feature`, after it has + * been created and styled. Useful for attaching events and popups to features. + * The default is to do nothing with the newly created layers: + * ```js + * function (feature, layer) {} + * ``` + * + * @option filter: Function = * + * A `Function` that will be used to decide whether to include a feature or not. + * The default is to include all features: + * ```js + * function (geoJsonFeature) { + * return true; + * } + * ``` + * Note: dynamically changing the `filter` option will have effect only on newly + * added data. It will _not_ re-evaluate already included features. + * + * @option coordsToLatLng: Function = * + * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. + * The default is the `coordsToLatLng` static method. + */ + + initialize: function (geojson, options) { + setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + // @method addData( data ): this + // Adds a GeoJSON object to the layer. + addData: function (geojson) { + var features = isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(feature); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return this; } + + var layer = geometryToLayer(geojson, options); + if (!layer) { + return this; + } + layer.feature = asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + // @method resetStyle( layer ): this + // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. + resetStyle: function (layer) { + // reset any custom styles + layer.options = extend({}, layer.defaultOptions); + this._setLayerStyle(layer, this.options.style); + return this; + }, + + // @method setStyle( style ): this + // Changes styles of GeoJSON vector layers with the given style function. + setStyle: function (style) { + return this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } +}); + +// @section +// There are several static functions which can be called without instantiating L.GeoJSON: + +// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer +// Creates a `Layer` from a given GeoJSON feature. Can use a custom +// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) +// functions if provided as options. +function geometryToLayer(geojson, options) { + + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry ? geometry.coordinates : null, + layers = [], + pointToLayer = options && options.pointToLayer, + _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, + latlng, latlngs, i, len; + + if (!coords && !geometry) { + return null; + } + + switch (geometry.type) { + case 'Point': + latlng = _coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = _coordsToLatLng(coords[i]); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng)); + } + return new FeatureGroup(layers); + + case 'LineString': + case 'MultiLineString': + latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); + return new Polyline(latlngs, options); + + case 'Polygon': + case 'MultiPolygon': + latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); + return new Polygon(latlngs, options); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + var layer = geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, options); + + if (layer) { + layers.push(layer); + } + } + return new FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } +} + +// @function coordsToLatLng(coords: Array): LatLng +// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) +// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. +function coordsToLatLng(coords) { + return new LatLng(coords[1], coords[0], coords[2]); +} + +// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array +// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. +// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). +// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. +function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { + var latlngs = []; + + for (var i = 0, len = coords.length, latlng; i < len; i++) { + latlng = levelsDeep ? + coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : + (_coordsToLatLng || coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; +} + +// @function latLngToCoords(latlng: LatLng, precision?: Number): Array +// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) +function latLngToCoords(latlng, precision) { + precision = typeof precision === 'number' ? precision : 6; + return latlng.alt !== undefined ? + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; +} + +// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array +// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) +// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. +function latLngsToCoords(latlngs, levelsDeep, closed, precision) { + var coords = []; + + for (var i = 0, len = latlngs.length; i < len; i++) { + coords.push(levelsDeep ? + latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : + latLngToCoords(latlngs[i], precision)); + } + + if (!levelsDeep && closed) { + coords.push(coords[0]); + } + + return coords; +} + +function getFeature(layer, newGeometry) { + return layer.feature ? + extend({}, layer.feature, {geometry: newGeometry}) : + asFeature(newGeometry); +} + +// @function asFeature(geojson: Object): Object +// Normalize GeoJSON geometries/features into GeoJSON features. +function asFeature(geojson) { + if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { + return geojson; + } + + return { + type: 'Feature', + properties: {}, + geometry: geojson + }; +} + +var PointToGeoJSON = { + toGeoJSON: function (precision) { + return getFeature(this, { + type: 'Point', + coordinates: latLngToCoords(this.getLatLng(), precision) + }); + } +}; + +// @namespace Marker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). +Marker.include(PointToGeoJSON); + +// @namespace CircleMarker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). +Circle.include(PointToGeoJSON); +CircleMarker.include(PointToGeoJSON); + + +// @namespace Polyline +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). +Polyline.include({ + toGeoJSON: function (precision) { + var multi = !isFlat(this._latlngs); + + var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); + } +}); + +// @namespace Polygon +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). +Polygon.include({ + toGeoJSON: function (precision) { + var holes = !isFlat(this._latlngs), + multi = holes && !isFlat(this._latlngs[0]); + + var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); + + if (!holes) { + coords = [coords]; + } + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); + } +}); + + +// @namespace LayerGroup +LayerGroup.include({ + toMultiPoint: function (precision) { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON(precision).geometry.coordinates); + }); + + return getFeature(this, { + type: 'MultiPoint', + coordinates: coords + }); + }, + + // @method toGeoJSON(): Object + // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). + toGeoJSON: function (precision) { + + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + + if (type === 'MultiPoint') { + return this.toMultiPoint(precision); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(precision); + if (isGeometryCollection) { + jsons.push(json.geometry); + } else { + var feature = asFeature(json); + // Squash nested feature collections + if (feature.type === 'FeatureCollection') { + jsons.push.apply(jsons, feature.features); + } else { + jsons.push(feature); + } + } + } + }); + + if (isGeometryCollection) { + return getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } +}); + +// @namespace GeoJSON +// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) +// Creates a GeoJSON layer. Optionally accepts an object in +// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map +// (you can alternatively add it later with `addData` method) and an `options` object. +function geoJSON(geojson, options) { + return new GeoJSON(geojson, options); +} + +// Backward compatibility. +var geoJson = geoJSON; + +/* + * @class ImageOverlay + * @aka L.ImageOverlay + * @inherits Interactive layer + * + * Used to load and display a single image over specific bounds of the map. Extends `Layer`. + * + * @example + * + * ```js + * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', + * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; + * L.imageOverlay(imageUrl, imageBounds).addTo(map); + * ``` + */ + +var ImageOverlay = Layer.extend({ + + // @section + // @aka ImageOverlay options + options: { + // @option opacity: Number = 1.0 + // The opacity of the image overlay. + opacity: 1, + + // @option alt: String = '' + // Text for the `alt` attribute of the image (useful for accessibility). + alt: '', + + // @option interactive: Boolean = false + // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. + interactive: false, + + // @option crossOrigin: Boolean = false + // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data. + crossOrigin: false, + + // @option errorOverlayUrl: String = '' + // URL to the overlay image to show in place of the overlay that failed to load. + errorOverlayUrl: '', + + // @option zIndex: Number = 1 + // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer. + zIndex: 1, + + // @option className: String = '' + // A custom class name to assign to the image. Empty by default. + className: '', + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = toLatLngBounds(bounds); + + setOptions(this, options); + }, + + onAdd: function () { + if (!this._image) { + this._initImage(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + + if (this.options.interactive) { + addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + + this.getPane().appendChild(this._image); + this._reset(); + }, + + onRemove: function () { + remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } + }, + + // @method setOpacity(opacity: Number): this + // Sets the opacity of the overlay. + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._image) { + this._updateOpacity(); + } + return this; + }, + + setStyle: function (styleOpts) { + if (styleOpts.opacity) { + this.setOpacity(styleOpts.opacity); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all overlays. + bringToFront: function () { + if (this._map) { + toFront(this._image); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all overlays. + bringToBack: function () { + if (this._map) { + toBack(this._image); + } + return this; + }, + + // @method setUrl(url: String): this + // Changes the URL of the image. + setUrl: function (url) { + this._url = url; + + if (this._image) { + this._image.src = url; + } + return this; + }, + + // @method setBounds(bounds: LatLngBounds): this + // Update the bounds that this ImageOverlay covers + setBounds: function (bounds) { + this._bounds = toLatLngBounds(bounds); + + if (this._map) { + this._reset(); + } + return this; + }, + + getEvents: function () { + var events = { + zoom: this._reset, + viewreset: this._reset + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @method: setZIndex(value: Number) : this + // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. + setZIndex: function (value) { + this.options.zIndex = value; + this._updateZIndex(); + return this; + }, + + // @method getBounds(): LatLngBounds + // Get the bounds that this ImageOverlay covers + getBounds: function () { + return this._bounds; + }, + + // @method getElement(): HTMLElement + // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) + // used by this overlay. + getElement: function () { + return this._image; + }, + + _initImage: function () { + var wasElementSupplied = this._url.tagName === 'IMG'; + var img = this._image = wasElementSupplied ? this._url : create$1('img'); + + addClass(img, 'leaflet-image-layer'); + if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } + if (this.options.className) { addClass(img, this.options.className); } + + img.onselectstart = falseFn; + img.onmousemove = falseFn; + + // @event load: Event + // Fired when the ImageOverlay layer has loaded its image + img.onload = bind(this.fire, this, 'load'); + img.onerror = bind(this._overlayOnError, this, 'error'); + + if (this.options.crossOrigin) { + img.crossOrigin = ''; + } + + if (this.options.zIndex) { + this._updateZIndex(); + } + + if (wasElementSupplied) { + this._url = img.src; + return; + } + + img.src = this._url; + img.alt = this.options.alt; + }, + + _animateZoom: function (e) { + var scale = this._map.getZoomScale(e.zoom), + offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; + + setTransform(this._image, offset, scale); + }, + + _reset: function () { + var image = this._image, + bounds = new Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast())), + size = bounds.getSize(); + + setPosition(image, bounds.min); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _updateOpacity: function () { + setOpacity(this._image, this.options.opacity); + }, + + _updateZIndex: function () { + if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._image.style.zIndex = this.options.zIndex; + } + }, + + _overlayOnError: function () { + // @event error: Event + // Fired when the ImageOverlay layer has loaded its image + this.fire('error'); + + var errorUrl = this.options.errorOverlayUrl; + if (errorUrl && this._url !== errorUrl) { + this._url = errorUrl; + this._image.src = errorUrl; + } + } +}); + +// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) +// Instantiates an image overlay object given the URL of the image and the +// geographical bounds it is tied to. +var imageOverlay = function (url, bounds, options) { + return new ImageOverlay(url, bounds, options); +}; + +/* + * @class VideoOverlay + * @aka L.VideoOverlay + * @inherits ImageOverlay + * + * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. + * + * A video overlay uses the [`