| 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you under the Apache License, Version 2.0 (the 
 | 
* "License"); you may not use this file except in compliance 
 | 
* with the License.  You may obtain a copy of the License at 
 | 
* 
 | 
*   http://www.apache.org/licenses/LICENSE-2.0 
 | 
* 
 | 
* Unless required by applicable law or agreed to in writing, 
 | 
* software distributed under the License is distributed on an 
 | 
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 | 
* KIND, either express or implied.  See the License for the 
 | 
* specific language governing permissions and limitations 
 | 
* under the License. 
 | 
*/ 
 | 
  
 | 
  
 | 
/** 
 | 
 * AUTO-GENERATED FILE. DO NOT MODIFY. 
 | 
 */ 
 | 
  
 | 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you under the Apache License, Version 2.0 (the 
 | 
* "License"); you may not use this file except in compliance 
 | 
* with the License.  You may obtain a copy of the License at 
 | 
* 
 | 
*   http://www.apache.org/licenses/LICENSE-2.0 
 | 
* 
 | 
* Unless required by applicable law or agreed to in writing, 
 | 
* software distributed under the License is distributed on an 
 | 
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 | 
* KIND, either express or implied.  See the License for the 
 | 
* specific language governing permissions and limitations 
 | 
* under the License. 
 | 
*/ 
 | 
import { normalizeToArray 
 | 
// , MappingExistingItem, setComponentTypeToKeyInfo, mappingToExists 
 | 
} from '../util/model.js'; 
 | 
import { each, clone, map, isTypedArray, setAsPrimitive, isArray, isObject 
 | 
// , HashMap , createHashMap, extend, merge, 
 | 
} from 'zrender/lib/core/util.js'; 
 | 
import { error } from '../util/log.js'; 
 | 
var QUERY_REG = /^(min|max)?(.+)$/; 
 | 
// Key: mainType 
 | 
// type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>; 
 | 
/** 
 | 
 * TERM EXPLANATIONS: 
 | 
 * See `ECOption` and `ECUnitOption` in `src/util/types.ts`. 
 | 
 */ 
 | 
var OptionManager = /** @class */function () { 
 | 
  // timeline.notMerge is not supported in ec3. Firstly there is rearly 
 | 
  // case that notMerge is needed. Secondly supporting 'notMerge' requires 
 | 
  // rawOption cloned and backuped when timeline changed, which does no 
 | 
  // good to performance. What's more, that both timeline and setOption 
 | 
  // method supply 'notMerge' brings complex and some problems. 
 | 
  // Consider this case: 
 | 
  // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); 
 | 
  // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); 
 | 
  function OptionManager(api) { 
 | 
    this._timelineOptions = []; 
 | 
    this._mediaList = []; 
 | 
    /** 
 | 
     * -1, means default. 
 | 
     * empty means no media. 
 | 
     */ 
 | 
    this._currentMediaIndices = []; 
 | 
    this._api = api; 
 | 
  } 
 | 
  OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) { 
 | 
    if (rawOption) { 
 | 
      // That set dat primitive is dangerous if user reuse the data when setOption again. 
 | 
      each(normalizeToArray(rawOption.series), function (series) { 
 | 
        series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data); 
 | 
      }); 
 | 
      each(normalizeToArray(rawOption.dataset), function (dataset) { 
 | 
        dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source); 
 | 
      }); 
 | 
    } 
 | 
    // Caution: some series modify option data, if do not clone, 
 | 
    // it should ensure that the repeat modify correctly 
 | 
    // (create a new object when modify itself). 
 | 
    rawOption = clone(rawOption); 
 | 
    // FIXME 
 | 
    // If some property is set in timeline options or media option but 
 | 
    // not set in baseOption, a warning should be given. 
 | 
    var optionBackup = this._optionBackup; 
 | 
    var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup); 
 | 
    this._newBaseOption = newParsedOption.baseOption; 
 | 
    // For setOption at second time (using merge mode); 
 | 
    if (optionBackup) { 
 | 
      // FIXME 
 | 
      // the restore merge solution is essentially incorrect. 
 | 
      // the mapping can not be 100% consistent with ecModel, which probably brings 
 | 
      // potential bug! 
 | 
      // The first merge is delayed, because in most cases, users do not call `setOption` twice. 
 | 
      // let fakeCmptsMap = this._fakeCmptsMap; 
 | 
      // if (!fakeCmptsMap) { 
 | 
      //     fakeCmptsMap = this._fakeCmptsMap = createHashMap(); 
 | 
      //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null); 
 | 
      // } 
 | 
      // mergeToBackupOption( 
 | 
      //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt 
 | 
      // ); 
 | 
      // For simplicity, timeline options and media options do not support merge, 
 | 
      // that is, if you `setOption` twice and both has timeline options, the latter 
 | 
      // timeline options will not be merged to the former, but just substitute them. 
 | 
      if (newParsedOption.timelineOptions.length) { 
 | 
        optionBackup.timelineOptions = newParsedOption.timelineOptions; 
 | 
      } 
 | 
      if (newParsedOption.mediaList.length) { 
 | 
        optionBackup.mediaList = newParsedOption.mediaList; 
 | 
      } 
 | 
      if (newParsedOption.mediaDefault) { 
 | 
        optionBackup.mediaDefault = newParsedOption.mediaDefault; 
 | 
      } 
 | 
    } else { 
 | 
      this._optionBackup = newParsedOption; 
 | 
    } 
 | 
  }; 
 | 
  OptionManager.prototype.mountOption = function (isRecreate) { 
 | 
    var optionBackup = this._optionBackup; 
 | 
    this._timelineOptions = optionBackup.timelineOptions; 
 | 
    this._mediaList = optionBackup.mediaList; 
 | 
    this._mediaDefault = optionBackup.mediaDefault; 
 | 
    this._currentMediaIndices = []; 
 | 
    return clone(isRecreate 
 | 
    // this._optionBackup.baseOption, which is created at the first `setOption` 
 | 
    // called, and is merged into every new option by inner method `mergeToBackupOption` 
 | 
    // each time `setOption` called, can be only used in `isRecreate`, because 
 | 
    // its reliability is under suspicion. In other cases option merge is 
 | 
    // performed by `model.mergeOption`. 
 | 
    ? optionBackup.baseOption : this._newBaseOption); 
 | 
  }; 
 | 
  OptionManager.prototype.getTimelineOption = function (ecModel) { 
 | 
    var option; 
 | 
    var timelineOptions = this._timelineOptions; 
 | 
    if (timelineOptions.length) { 
 | 
      // getTimelineOption can only be called after ecModel inited, 
 | 
      // so we can get currentIndex from timelineModel. 
 | 
      var timelineModel = ecModel.getComponent('timeline'); 
 | 
      if (timelineModel) { 
 | 
        option = clone( 
 | 
        // FIXME:TS as TimelineModel or quivlant interface 
 | 
        timelineOptions[timelineModel.getCurrentIndex()]); 
 | 
      } 
 | 
    } 
 | 
    return option; 
 | 
  }; 
 | 
  OptionManager.prototype.getMediaOption = function (ecModel) { 
 | 
    var ecWidth = this._api.getWidth(); 
 | 
    var ecHeight = this._api.getHeight(); 
 | 
    var mediaList = this._mediaList; 
 | 
    var mediaDefault = this._mediaDefault; 
 | 
    var indices = []; 
 | 
    var result = []; 
 | 
    // No media defined. 
 | 
    if (!mediaList.length && !mediaDefault) { 
 | 
      return result; 
 | 
    } 
 | 
    // Multi media may be applied, the latter defined media has higher priority. 
 | 
    for (var i = 0, len = mediaList.length; i < len; i++) { 
 | 
      if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { 
 | 
        indices.push(i); 
 | 
      } 
 | 
    } 
 | 
    // FIXME 
 | 
    // Whether mediaDefault should force users to provide? Otherwise 
 | 
    // the change by media query can not be recorvered. 
 | 
    if (!indices.length && mediaDefault) { 
 | 
      indices = [-1]; 
 | 
    } 
 | 
    if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { 
 | 
      result = map(indices, function (index) { 
 | 
        return clone(index === -1 ? mediaDefault.option : mediaList[index].option); 
 | 
      }); 
 | 
    } 
 | 
    // Otherwise return nothing. 
 | 
    this._currentMediaIndices = indices; 
 | 
    return result; 
 | 
  }; 
 | 
  return OptionManager; 
 | 
}(); 
 | 
/** 
 | 
 * [RAW_OPTION_PATTERNS] 
 | 
 * (Note: "series: []" represents all other props in `ECUnitOption`) 
 | 
 * 
 | 
 * (1) No prop "baseOption" declared: 
 | 
 * Root option is used as "baseOption" (except prop "options" and "media"). 
 | 
 * ```js 
 | 
 * option = { 
 | 
 *     series: [], 
 | 
 *     timeline: {}, 
 | 
 *     options: [], 
 | 
 * }; 
 | 
 * option = { 
 | 
 *     series: [], 
 | 
 *     media: {}, 
 | 
 * }; 
 | 
 * option = { 
 | 
 *     series: [], 
 | 
 *     timeline: {}, 
 | 
 *     options: [], 
 | 
 *     media: {}, 
 | 
 * } 
 | 
 * ``` 
 | 
 * 
 | 
 * (2) Prop "baseOption" declared: 
 | 
 * If "baseOption" declared, `ECUnitOption` props can only be declared 
 | 
 * inside "baseOption" except prop "timeline" (compat ec2). 
 | 
 * ```js 
 | 
 * option = { 
 | 
 *     baseOption: { 
 | 
 *         timeline: {}, 
 | 
 *         series: [], 
 | 
 *     }, 
 | 
 *     options: [] 
 | 
 * }; 
 | 
 * option = { 
 | 
 *     baseOption: { 
 | 
 *         series: [], 
 | 
 *     }, 
 | 
 *     media: [] 
 | 
 * }; 
 | 
 * option = { 
 | 
 *     baseOption: { 
 | 
 *         timeline: {}, 
 | 
 *         series: [], 
 | 
 *     }, 
 | 
 *     options: [] 
 | 
 *     media: [] 
 | 
 * }; 
 | 
 * option = { 
 | 
 *     // ec3 compat ec2: allow (only) `timeline` declared 
 | 
 *     // outside baseOption. Keep this setting for compat. 
 | 
 *     timeline: {}, 
 | 
 *     baseOption: { 
 | 
 *         series: [], 
 | 
 *     }, 
 | 
 *     options: [], 
 | 
 *     media: [] 
 | 
 * }; 
 | 
 * ``` 
 | 
 */ 
 | 
function parseRawOption( 
 | 
// `rawOption` May be modified 
 | 
rawOption, optionPreprocessorFuncs, isNew) { 
 | 
  var mediaList = []; 
 | 
  var mediaDefault; 
 | 
  var baseOption; 
 | 
  var declaredBaseOption = rawOption.baseOption; 
 | 
  // Compatible with ec2, [RAW_OPTION_PATTERNS] above. 
 | 
  var timelineOnRoot = rawOption.timeline; 
 | 
  var timelineOptionsOnRoot = rawOption.options; 
 | 
  var mediaOnRoot = rawOption.media; 
 | 
  var hasMedia = !!rawOption.media; 
 | 
  var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline); 
 | 
  if (declaredBaseOption) { 
 | 
    baseOption = declaredBaseOption; 
 | 
    // For merge option. 
 | 
    if (!baseOption.timeline) { 
 | 
      baseOption.timeline = timelineOnRoot; 
 | 
    } 
 | 
  } 
 | 
  // For convenience, enable to use the root option as the `baseOption`: 
 | 
  // `{ ...normalOptionProps, media: [{ ... }, { ... }] }` 
 | 
  else { 
 | 
    if (hasTimeline || hasMedia) { 
 | 
      rawOption.options = rawOption.media = null; 
 | 
    } 
 | 
    baseOption = rawOption; 
 | 
  } 
 | 
  if (hasMedia) { 
 | 
    if (isArray(mediaOnRoot)) { 
 | 
      each(mediaOnRoot, function (singleMedia) { 
 | 
        if (process.env.NODE_ENV !== 'production') { 
 | 
          // Real case of wrong config. 
 | 
          if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) { 
 | 
            error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }'); 
 | 
          } 
 | 
        } 
 | 
        if (singleMedia && singleMedia.option) { 
 | 
          if (singleMedia.query) { 
 | 
            mediaList.push(singleMedia); 
 | 
          } else if (!mediaDefault) { 
 | 
            // Use the first media default. 
 | 
            mediaDefault = singleMedia; 
 | 
          } 
 | 
        } 
 | 
      }); 
 | 
    } else { 
 | 
      if (process.env.NODE_ENV !== 'production') { 
 | 
        // Real case of wrong config. 
 | 
        error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }'); 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  doPreprocess(baseOption); 
 | 
  each(timelineOptionsOnRoot, function (option) { 
 | 
    return doPreprocess(option); 
 | 
  }); 
 | 
  each(mediaList, function (media) { 
 | 
    return doPreprocess(media.option); 
 | 
  }); 
 | 
  function doPreprocess(option) { 
 | 
    each(optionPreprocessorFuncs, function (preProcess) { 
 | 
      preProcess(option, isNew); 
 | 
    }); 
 | 
  } 
 | 
  return { 
 | 
    baseOption: baseOption, 
 | 
    timelineOptions: timelineOptionsOnRoot || [], 
 | 
    mediaDefault: mediaDefault, 
 | 
    mediaList: mediaList 
 | 
  }; 
 | 
} 
 | 
/** 
 | 
 * @see <http://www.w3.org/TR/css3-mediaqueries/#media1> 
 | 
 * Support: width, height, aspectRatio 
 | 
 * Can use max or min as prefix. 
 | 
 */ 
 | 
function applyMediaQuery(query, ecWidth, ecHeight) { 
 | 
  var realMap = { 
 | 
    width: ecWidth, 
 | 
    height: ecHeight, 
 | 
    aspectratio: ecWidth / ecHeight // lower case for convenience. 
 | 
  }; 
 | 
  var applicable = true; 
 | 
  each(query, function (value, attr) { 
 | 
    var matched = attr.match(QUERY_REG); 
 | 
    if (!matched || !matched[1] || !matched[2]) { 
 | 
      return; 
 | 
    } 
 | 
    var operator = matched[1]; 
 | 
    var realAttr = matched[2].toLowerCase(); 
 | 
    if (!compare(realMap[realAttr], value, operator)) { 
 | 
      applicable = false; 
 | 
    } 
 | 
  }); 
 | 
  return applicable; 
 | 
} 
 | 
function compare(real, expect, operator) { 
 | 
  if (operator === 'min') { 
 | 
    return real >= expect; 
 | 
  } else if (operator === 'max') { 
 | 
    return real <= expect; 
 | 
  } else { 
 | 
    // Equals 
 | 
    return real === expect; 
 | 
  } 
 | 
} 
 | 
function indicesEquals(indices1, indices2) { 
 | 
  // indices is always order by asc and has only finite number. 
 | 
  return indices1.join(',') === indices2.join(','); 
 | 
} 
 | 
/** 
 | 
 * Consider case: 
 | 
 * `chart.setOption(opt1);` 
 | 
 * Then user do some interaction like dataZoom, dataView changing. 
 | 
 * `chart.setOption(opt2);` 
 | 
 * Then user press 'reset button' in toolbox. 
 | 
 * 
 | 
 * After doing that all of the interaction effects should be reset, the 
 | 
 * chart should be the same as the result of invoke 
 | 
 * `chart.setOption(opt1); chart.setOption(opt2);`. 
 | 
 * 
 | 
 * Although it is not able ensure that 
 | 
 * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to 
 | 
 * `chart.setOption(merge(opt1, opt2));` exactly, 
 | 
 * this might be the only simple way to implement that feature. 
 | 
 * 
 | 
 * MEMO: We've considered some other approaches: 
 | 
 * 1. Each model handles its self restoration but not uniform treatment. 
 | 
 *     (Too complex in logic and error-prone) 
 | 
 * 2. Use a shadow ecModel. (Performance expensive) 
 | 
 * 
 | 
 * FIXME: A possible solution: 
 | 
 * Add a extra level of model for each component model. The inheritance chain would be: 
 | 
 * ecModel <- componentModel <- componentActionModel <- dataItemModel 
 | 
 * And all of the actions can only modify the `componentActionModel` rather than 
 | 
 * `componentModel`. `setOption` will only modify the `ecModel` and `componentModel`. 
 | 
 * When "resotre" action triggered, model from `componentActionModel` will be discarded 
 | 
 * instead of recreating the "ecModel" from the "_optionBackup". 
 | 
 */ 
 | 
// function mergeToBackupOption( 
 | 
//     fakeCmptsMap: FakeComponentsMap, 
 | 
//     // `tarOption` Can be null/undefined, means init 
 | 
//     tarOption: ECUnitOption, 
 | 
//     newOption: ECUnitOption, 
 | 
//     // Can be null/undefined 
 | 
//     opt: InnerSetOptionOpts 
 | 
// ): void { 
 | 
//     newOption = newOption || {} as ECUnitOption; 
 | 
//     const notInit = !!tarOption; 
 | 
//     each(newOption, function (newOptsInMainType, mainType) { 
 | 
//         if (newOptsInMainType == null) { 
 | 
//             return; 
 | 
//         } 
 | 
//         if (!ComponentModel.hasClass(mainType)) { 
 | 
//             if (tarOption) { 
 | 
//                 tarOption[mainType] = merge(tarOption[mainType], newOptsInMainType, true); 
 | 
//             } 
 | 
//         } 
 | 
//         else { 
 | 
//             const oldTarOptsInMainType = notInit ? normalizeToArray(tarOption[mainType]) : null; 
 | 
//             const oldFakeCmptsInMainType = fakeCmptsMap.get(mainType) || []; 
 | 
//             const resultTarOptsInMainType = notInit ? (tarOption[mainType] = [] as ComponentOption[]) : null; 
 | 
//             const resultFakeCmptsInMainType = fakeCmptsMap.set(mainType, []); 
 | 
//             const mappingResult = mappingToExists( 
 | 
//                 oldFakeCmptsInMainType, 
 | 
//                 normalizeToArray(newOptsInMainType), 
 | 
//                 (opt && opt.replaceMergeMainTypeMap.get(mainType)) ? 'replaceMerge' : 'normalMerge' 
 | 
//             ); 
 | 
//             setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel as ComponentModelConstructor); 
 | 
//             each(mappingResult, function (resultItem, index) { 
 | 
//                 // The same logic as `Global.ts#_mergeOption`. 
 | 
//                 let fakeCmpt = resultItem.existing; 
 | 
//                 const newOption = resultItem.newOption; 
 | 
//                 const keyInfo = resultItem.keyInfo; 
 | 
//                 let fakeCmptOpt; 
 | 
//                 if (!newOption) { 
 | 
//                     fakeCmptOpt = oldTarOptsInMainType[index]; 
 | 
//                 } 
 | 
//                 else { 
 | 
//                     if (fakeCmpt && fakeCmpt.subType === keyInfo.subType) { 
 | 
//                         fakeCmpt.name = keyInfo.name; 
 | 
//                         if (notInit) { 
 | 
//                             fakeCmptOpt = merge(oldTarOptsInMainType[index], newOption, true); 
 | 
//                         } 
 | 
//                     } 
 | 
//                     else { 
 | 
//                         fakeCmpt = extend({}, keyInfo); 
 | 
//                         if (notInit) { 
 | 
//                             fakeCmptOpt = clone(newOption); 
 | 
//                         } 
 | 
//                     } 
 | 
//                 } 
 | 
//                 if (fakeCmpt) { 
 | 
//                     notInit && resultTarOptsInMainType.push(fakeCmptOpt); 
 | 
//                     resultFakeCmptsInMainType.push(fakeCmpt); 
 | 
//                 } 
 | 
//                 else { 
 | 
//                     notInit && resultTarOptsInMainType.push(void 0); 
 | 
//                     resultFakeCmptsInMainType.push(void 0); 
 | 
//                 } 
 | 
//             }); 
 | 
//         } 
 | 
//     }); 
 | 
// } 
 | 
export default OptionManager; 
 |