diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml index 6e595120..b73542fe 100644 --- a/.buildkite/pipeline.yaml +++ b/.buildkite/pipeline.yaml @@ -1,16 +1,39 @@ steps: - - label: ":books: Build spec" + - label: ":snake: Build swagger definitions for matrix.org" command: - - python3 -m venv env - - env/bin/pip install -r scripts/requirements.txt - - ". env/bin/activate; scripts/generate-matrix-org-assets" + # Install the python dependencies necessary to build the spec + - python3 -m venv env && . env/bin/activate + - pip install -r scripts/requirements.txt + # Build the spec + - scripts/generate-matrix-org-assets artifact_paths: - assets.tar.gz plugins: - - docker#v3.0.1: - image: "python:3.6" + - docker#v3.7.0: + image: python:3.9 - label: "rebuild matrix.org" trigger: "matrix-dot-org" async: true branches: "master" + + - label: ":books: Build the spec" + command: + # Install package dependencies + - apk add nodejs npm git hugo + # Install the node dependencies necessary to build the spec + - npm i + # Pull all git submodules, required for the hugo theme + - git submodule update --init --recursive + # Pull current proposal information + - npm run get-proposals + # Build the spec, will build to './spec' + # Set the baseURL as we're deploying to https://spec.matrix.org/unstable + - hugo --baseURL "/unstable" -d "spec" + # Compress the result and make it available as an artifact + - tar -czf spec.tar.gz spec + artifact_paths: + - spec.tar.gz + plugins: + - docker#v3.7.0: + image: alpine \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index bf4404ce..e0815d76 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,13 +1,19 @@ gendoc: &gendoc name: Generate the docs + # Note: Node dependencies are required for the hugo build. + # Note: We use a custom config file for circleci due to some specifics with hosting the + # site using CircleCI's artifacts platform. See config-circleci.toml for details. command: | - source /env/bin/activate - scripts/gendoc.py + apk add nodejs npm git hugo + npm i + cat config-circleci.toml config.toml > hugo-config.toml + hugo --config hugo-config.toml --baseURL "/${CIRCLE_NODE_INDEX}/public" genswagger: &genswagger - name: Generate the swagger + name: Validate sources and generate swagger json command: | source /env/bin/activate + scripts/check-swagger-sources.py scripts/dump-swagger.py buildswaggerui: &buildswaggerui @@ -27,23 +33,14 @@ checkexamples: &checkexamples name: Check Event Examples command: | source /env/bin/activate - cd event-schemas - ./check_examples.py - cd ../api - ./check_examples.py - -genmatrixassets: &genmatrixassets - name: Generate/Verify matrix.org assets - command: | - source /env/bin/activate - ./scripts/generate-matrix-org-assets + scripts/check-event-schema-examples.py validateapi: &validateapi name: Validate OpenAPI specifications command: | - cd api + cd scripts npm install - node validator.js -s "client-server" + node validator.js -s "../data/api/client-server" buildspeculator: &buildspeculator name: Build Speculator @@ -51,12 +48,6 @@ buildspeculator: &buildspeculator cd scripts/speculator go build -v -buildcontinuserv: &buildcontinuserv - name: Build Continuserv - command: | - cd scripts/continuserv - go build -v - version: 2 jobs: validate-docs: @@ -71,18 +62,21 @@ jobs: steps: - checkout - run: *checkexamples - - run: *genmatrixassets # We don't actually use the assets, but we do want to make sure they build build-docs: docker: - - image: uhoreg/matrix-doc-build + - image: alpine steps: + # Note: We install git in the image so we can pull git submodules. The hugo theme in use + # is a git submodule, which has its own submodules, and all need to be loaded. + - run: apk add git - checkout + - run: git submodule update --init --recursive - run: *gendoc - store_artifacts: - path: scripts/gen + path: public - run: name: "Doc build is available at:" - command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/scripts/gen/index.html"; echo $DOCS_URL + command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/public/index.html"; echo $DOCS_URL build-swagger: docker: - image: uhoreg/matrix-doc-build @@ -104,8 +98,6 @@ jobs: name: Install Dependencies command: | go get -v github.com/hashicorp/golang-lru - go get -v gopkg.in/fsnotify/fsnotify.v1 - - run: *buildcontinuserv - run: *buildspeculator workflows: diff --git a/.github/ISSUE_TEMPLATE/clarification.md b/.github/ISSUE_TEMPLATE/clarification.md new file mode 100644 index 00000000..1aaa35c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/clarification.md @@ -0,0 +1,13 @@ +--- +name: Clarity problem +about: Report an area of the spec that is unclear. +title: '' +labels: 'clarification' +assignees: '' + +--- + +**Link to problem area**: + +**Issue** +What is wrong? How can we improve? diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 00000000..79bc995d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Matrix Spec Discussion + url: "https://matrix.to/#/#matrix-spec:matrix.org" + about: Questions about the spec and proposal process can be asked here. + - name: Matrix Security Policy + url: https://www.matrix.org/security-disclosure-policy/ + about: Learn more about our security disclosure policy. diff --git a/.github/ISSUE_TEMPLATE/cosmetic-bug.md b/.github/ISSUE_TEMPLATE/cosmetic-bug.md new file mode 100644 index 00000000..1012302b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/cosmetic-bug.md @@ -0,0 +1,13 @@ +--- +name: Cosmetic issue +about: Report an issue with how the spec looks. +title: '' +labels: 'aesthetic' +assignees: '' + +--- + +**Link to problem area**: + +**Issue** +What is wrong? What can we do to improve? diff --git a/.github/ISSUE_TEMPLATE/idea.md b/.github/ISSUE_TEMPLATE/idea.md new file mode 100644 index 00000000..028012a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/idea.md @@ -0,0 +1,12 @@ +--- +name: Spec idea +about: Suggest a future MSC idea. +title: '' +labels: 'improvement' +assignees: '' + +--- + +**Suggestion** +What would you like to see in Matrix? If your idea is vaguely complete enough, we +recommend submitting [an MSC](https://matrix.org/docs/spec/proposals) instead. diff --git a/.github/ISSUE_TEMPLATE/spec-bug.md b/.github/ISSUE_TEMPLATE/spec-bug.md new file mode 100644 index 00000000..590234ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/spec-bug.md @@ -0,0 +1,16 @@ +--- +name: Documentation error +about: Report an issue with the spec itself (incorrect text). +title: '' +labels: 'spec-bug' +assignees: '' + +--- + +**Link to problem area**: + +**Issue** +What is wrong? + +**Expected behaviour** +How can the issue be fixed? Links to implementations/documents which prove the spec is wrong are appreciated. diff --git a/.gitignore b/.gitignore index 9cc27b85..cce5848f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,21 @@ /api/node_modules /assets /assets.tar.gz +/data/msc /env* +/node_modules +/resources /scripts/gen /scripts/continuserv/continuserv /scripts/speculator/speculator /scripts/swagger /scripts/tmp /templating/out +/hugo-config.toml +/public *.pyc *.swp _rendered.rst /.vscode/ /.idea/ +/spec/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..5e606ab2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "themes/docsy"] + path = themes/docsy + url = https://github.com/matrix-org/docsy.git + branch = master diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2d26e8a8..2e72e186 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -29,9 +29,7 @@ some time to complete. Changes to the protocol (new endpoints, ideas, etc) need to go through the `proposals process `_. Other changes, such as fixing bugs, typos, or clarifying existing behaviour do not need a proposal. -If you're not sure, visit us at `#matrix-spec:matrix.org`_ -and ask. - +If you're not sure, visit us at `#matrix-spec:matrix.org`_ and ask. Other changes ~~~~~~~~~~~~~ @@ -64,12 +62,17 @@ following: to fix. On the other hand, introducing new behaviour is best represented by a proposal. +* Design or aesthetic changes, such as improving accessibility, colour schemes, + etc. Please check in with us at `#matrix-docs:matrix.org`_ with your proposed + design change before opening a PR so we can work with you on it. + For such changes, please do just open a `pull request`_. If you're not sure if your change is covered by the above, please visit `#matrix-spec:matrix.org` and ask. .. _`pull request`: https://help.github.com/articles/about-pull-requests .. _`#matrix-spec:matrix.org`: https://matrix.to/#/#matrix-spec:matrix.org +.. _`#matrix-docs:matrix.org`: https://matrix.to/#/#matrix-docs:matrix.org Adding to the changelog @@ -86,8 +89,8 @@ To create a changelog entry, create a file named in the format ``prNumber.type`` the ``newsfragments`` directory. The ``type`` can be one of the following: * ``new`` - Used when adding new endpoints. Please have the file contents be the - method and route being added, surrounded in RST code tags. For example: ``POST - /accounts/whoami`` + method and route being added, surrounded in markdown code tags. For example: \`POST + /accounts/whoami\`. * ``feature`` - Used when adding backwards-compatible changes to the API. @@ -100,8 +103,7 @@ the ``newsfragments`` directory. The ``type`` can be one of the following: All news fragments must have a brief summary explaining the change in the contents of the file. The summary must end in a full stop to be in line with -the style guide and and formatting must be done using `Restructured Text -`_. +the style guide and formatting must be done using Markdown. Changes that do not change the spec, such as changes to the build script, formatting, CSS, etc should not get a news fragment. diff --git a/README.md b/README.md new file mode 100644 index 00000000..a8bed3ce --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Matrix Specification + +This repository contains the Matrix Specification, rendered at [spec.matrix.org](http://spec.matrix.org/). + +Developers looking to use Matrix should join [#matrix-dev:matrix.org](https://matrix.to/#/#matrix-dev:matrix.org) +on Matrix for help. + +Spec authors and proposal writers are welcome to join [#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org). +We welcome contributions! See [CONTRIBUTING.rst](./CONTRIBUTING.rst) for details. + +## Structure + +The Matrix spec is compiled with [Hugo](https://gohugo.io/) (a static site generator) with the following structure: + +* `/assets`: assets that need postprocessing using [Hugo Pipes](https://gohugo.io/hugo-pipes/introduction/). + For example, Sass files would go here. + +* `/content`: files that will become pages in the site go here. Typically these are Markdown files with some YAML front + matter indicating, [among other things](https://gohugo.io/content-management/front-matter/), what layout should be + applied to this page. The organization of files under `/content` determines the organization of pages in the built + site. + +* `/data`: this can contain TOML, YAML, or JSON files. Files kept here are directly available to template code as + [data objects](https://gohugo.io/templates/data-templates/), so templates don't need to load them from a file and + parse them. This is also where our Swagger/OpenAPI definitions and schemas are. + +* `/layouts`: this contains [Hugo templates](https://gohugo.io/templates/). Some templates define the overall layout of + a page: for example, whether it has header, footer, sidebar, and so on. + * `/layouts/partials`: these templates can be called from other templates, so they can be used to factor out + template code that's used in more than one template. An obvious example here is something like a sidebar, where + several different page layouts might all include the sidebar. But also, partial templates can return values: this + means they can be used like functions, that can be called by multiple templates to do some common processing. + * `/layouts/shortcodes`: these templates can be called directly from files in `/content`. + +* `/static`: static files which don't need preprocessing. JS or CSS files could live here. + +* `/themes`: you can use just Hugo or use it with a theme. Themes primarily provide additional templates, which are + supplied in a `/themes/$theme_name/layouts` directory. You can use a theme but customise it by providing your own + versions of any of the theme layouts in the base `/layouts` directory. That is, if a theme provides + `/themes/$theme_name/layouts/sidebar.html` and you provide `/layouts/sidebar.html`, then your version of the + template will be used. + +It also has the following top-level file: + +* `config.toml`: site-wide configuration settings. Some of these are built-in and you can add your own. Config settings + defined here are available in templates. All these directories above are configurable via `config.toml` settings. + +Additionally, the following directories may be of interest: + +* `/attic`: Here contains historical sections of specification and legacy drafts for the specification. +* `/changelogs`: Various bits of changelog for the specification areas. +* `/data-definitions`: Bits of structured data consumable by Matrix implementations. +* `/meta`: Documentation relating to the spec's processes that are otherwise untracked (release instructions, etc). +* `/scripts`: Various scripts for generating the spec and validating its contents. +* `/proposals`: Matrix Spec Change (MSC) proposals. See . + +## Authoring changes to the spec + +Please read [CONTRIBUTING.rst](./CONTRIBUTING.rst) before authoring a change to the spec. Note that spec authoring takes +place after an MSC has been accepted, not as part of a proposal itself. + +1. Install the extended version (often the OS default) of Hugo: +2. Run `git submodule update --init --recursive` for good measure. +3. Run `npm i` to install the dependencies. Note that this will require NodeJS to be installed. +4. Run `npm run get-proposals` to seed proposal data. This is merely for populating the content of the "Spec Change Proposals" + page and is not required. +5. Run `hugo serve` to run a local webserver which builds whenever a file change is detected. If watching doesn't appear + to be working for you, try `hugo serve --disableFastRender` instead. +6. Edit the specification 🙂 + +We use a highly customized [Docsy](https://www.docsy.dev/) theme for our generated site, which uses Bootstrap and Font +Awesome. If you're looking at making design-related changes to the spec site, please coordinate with us in +[#matrix-docs:matrix.org](https://matrix.to/#/#matrix-docs:matrix.org) before opening a PR. + +## Building the specification + +If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above +steps for authoring changes to the specification and instead of `hugo serve` run `hugo -d "spec"` - this will generate the +spec to `/spec`. If you'd like to serve the spec off a path instead of a domain root (eg: `/unstable`), add `--baseURL "/unstable"` +to the `hugo -d "spec"` command. + +For building the swagger definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt` +and finally `python ./scripts/dump-swagger.py` to generate it to `./scripts/swagger/api-docs.json`. To make use of the generated file, +there are a number of options: + +* It can be uploaded from your filesystem to an online editor/viewer such as [on the swagger website](http://editor.swagger.io/). +* You can run a local HTTP server by running `./scripts/swagger-http-server.py`, and then view the documentation via an + online viewer; for example, at . +* You can host the swagger UI yourself. See for advice on how to + do so. + +## Issue tracking + +Specification issues are tracked on github at . + +See [meta/github-labels.rst](./meta/github-labels.rst) for information on what the labels mean. diff --git a/README.rst b/README.rst deleted file mode 100644 index 61c27f15..00000000 --- a/README.rst +++ /dev/null @@ -1,143 +0,0 @@ -This repository contains the Matrix specification. - -If you want to ask more about the specification, join us on -`#matrix-dev:matrix.org `_. - -We welcome contributions to the spec! See the notes below on `Building the -specification`_, and ``_ to get started making contributions. - -Note that the Matrix Project lists, which were previously kept in this -repository, are now in https://github.com/matrix-org/matrix.org. - -Structure of this repository -============================ - -- ``api`` : `OpenAPI`_ (swagger) specifications for the the HTTP APIs. -- ``attic``: historical sections of specification for reference - purposes. -- ``changelogs``: change logs for the various parts of the - specification. -- ``drafts``: Previously, contained documents which were under discussion for - future incusion into the specification and/or supporting documentation. This - is now historical, as we use separate discussion documents (see - ``_). -- ``event-schemas``: the `JSON Schema`_ for all Matrix events - contained in the specification, along with example JSON files. -- ``meta``: documents outlining the processes involved when writing - documents, e.g. documentation style, guidelines. -- ``scripts``: scripts to generate formatted versions of the - documentation, typically HTML. -- ``specification``: the specification split up into sections. - -.. _OpenAPI: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md -.. _JSON Schema: http://json-schema.org/ - -Building the specification -========================== - -The Matrix Spec is generated by a set of scripts, from the RST documents, API -specs and event schemas in this repository. - -Preparation ------------ - -To use the scripts, it is best to create a Python 3.4+ virtualenv as follows:: - - virtualenv -p python3 env - env/bin/pip install -r scripts/requirements.txt - -(Benjamin Saunders has contributed a script for `Nix`_ users, which can be -invoked with ``nix-shell scripts/contrib/shell.nix``.) - -.. TODO: Possibly we need some libs installed; should record what they are. - -.. _`Nix`: https://nixos.org/nix/ - -Generating the specification ----------------------------- - -To rebuild the specification, use ``scripts/gendoc.py``:: - - source env/bin/activate - ./scripts/gendoc.py - -The above will write the rendered version of the specification to -``scripts/gen``. To view it, point your browser at ``scripts/gen/index.html``. - -Windows users -~~~~~~~~~~~~~ -The ``source`` program does not exist on Windows, so instead run one of the -``activate`` files in ``.\env\Scripts\`` to activate the virtual environment. - -If you're on Windows Vista or higher, be sure that the "Symbolic Links" -option was selected when installing Git prior to cloning this repository. If -you're still seeing errors about files not being found it is likely because -the symlink at ``api/client-server/definitions/event-schemas`` looks like a -file. To correct the problem, open an Administrative/Elevated Command Prompt in your -cloned matrix-doc directory and run the following:: - - cd api\client-server\definitions - del event-schemas - mklink /D event-schemas "..\..\..\event-schemas" - -This will delete the file and replace it with a symlink. Git should not detect -this as a change, and you should be able to go back to building the project. - -Generating the OpenAPI (Swagger) specs --------------------------------------- - -`Swagger`_ is a framework for representing RESTful APIs. We use it to generate -interactive documentation for our APIs. - -Before the Swagger docs can be used in the Swagger UI (or other tool expecting -a Swagger specs, they must be combined into a single json file. This can be -done as follows:: - - source env/bin/activate - ./scripts/dump-swagger.py - -By default, ``dump-swagger`` will write to ``scripts/swagger/api-docs.json``. - -To make use of the generated file, there are a number of options: - -* It can be uploaded from your filesystem to an online editor/viewer such as - http://editor.swagger.io/ -* You can run a local HTTP server by running - ``./scripts/swagger-http-server.py``, and then view the documentation via an - online viewer; for example, at - http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json -* You can host the swagger UI yourself. See - https://github.com/swagger-api/swagger-ui#how-to-run for advice on how to do - so. - -.. _`Swagger`: http://swagger.io/ - -Continuserv ------------ - -Continuserv is a script which will rebuild the specification every time a file -is changed, and will serve it to a browser over HTTP. It is intended for use by -specification authors, so that they can quickly see the effects of their -changes. - -It is written in Go, so you will need the ``go`` compiler installed on your -computer. You will also need to install fsnotify by running:: - - go get gopkg.in/fsnotify/fsnotify.v1 - -Then, create a virtualenv as described above under `Preparation`_, -and:: - - source env/bin/activate - go run ./scripts/continuserv/main.go - -You will then be able to view the generated spec by visiting -http://localhost:8000/index.html. - -Issue tracking -============== - -Issues with the Matrix specification are tracked in `GitHub -`_. - -See `meta/github-labels.rst `_ for notes on what the labels mean. diff --git a/api/README b/api/README deleted file mode 100644 index 7b971fac..00000000 --- a/api/README +++ /dev/null @@ -1,2 +0,0 @@ -This directory contains swagger-compatible representations of our APIs. See -the main README.rst for details on how to make use of them. diff --git a/api/client-server/definitions/event-schemas b/api/client-server/definitions/event-schemas deleted file mode 120000 index 376cf90c..00000000 --- a/api/client-server/definitions/event-schemas +++ /dev/null @@ -1 +0,0 @@ -../../../event-schemas \ No newline at end of file diff --git a/api/files/backbone-min.js b/api/files/backbone-min.js deleted file mode 100644 index c1c0d4ff..00000000 --- a/api/files/backbone-min.js +++ /dev/null @@ -1,38 +0,0 @@ -// Backbone.js 0.9.2 - -// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org -(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= -{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= -z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= -{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== -b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: -b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; -a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, -h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); -return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= -{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| -!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); -this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('